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.
- package/LICENSE +8 -0
- package/README.md +248 -0
- package/commands/millhouse.md +223 -0
- package/dist/analysis/graph-builder.d.ts +42 -0
- package/dist/analysis/graph-builder.d.ts.map +1 -0
- package/dist/analysis/graph-builder.js +98 -0
- package/dist/analysis/graph-builder.js.map +1 -0
- package/dist/analysis/issue-analyzer.d.ts +20 -0
- package/dist/analysis/issue-analyzer.d.ts.map +1 -0
- package/dist/analysis/issue-analyzer.js +167 -0
- package/dist/analysis/issue-analyzer.js.map +1 -0
- package/dist/analysis/plan-parser.d.ts +8 -0
- package/dist/analysis/plan-parser.d.ts.map +1 -0
- package/dist/analysis/plan-parser.js +112 -0
- package/dist/analysis/plan-parser.js.map +1 -0
- package/dist/cli/cleanup.d.ts +20 -0
- package/dist/cli/cleanup.d.ts.map +1 -0
- package/dist/cli/cleanup.js +186 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/commands/clean.d.ts +2 -0
- package/dist/cli/commands/clean.d.ts.map +1 -0
- package/dist/cli/commands/clean.js +16 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +2 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +82 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +10 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +352 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +6 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +74 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/status.d.ts +7 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +83 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/progress-display.d.ts +93 -0
- package/dist/cli/progress-display.d.ts.map +1 -0
- package/dist/cli/progress-display.js +318 -0
- package/dist/cli/progress-display.js.map +1 -0
- package/dist/core/orchestrator.d.ts +79 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +389 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/scheduler.d.ts +72 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +234 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/execution/claude-runner.d.ts +41 -0
- package/dist/execution/claude-runner.d.ts.map +1 -0
- package/dist/execution/claude-runner.js +241 -0
- package/dist/execution/claude-runner.js.map +1 -0
- package/dist/execution/worktree-manager.d.ts +52 -0
- package/dist/execution/worktree-manager.d.ts.map +1 -0
- package/dist/execution/worktree-manager.js +208 -0
- package/dist/execution/worktree-manager.js.map +1 -0
- package/dist/github/client.d.ts +27 -0
- package/dist/github/client.d.ts.map +1 -0
- package/dist/github/client.js +178 -0
- package/dist/github/client.js.map +1 -0
- package/dist/github/issue-discoverer.d.ts +20 -0
- package/dist/github/issue-discoverer.d.ts.map +1 -0
- package/dist/github/issue-discoverer.js +83 -0
- package/dist/github/issue-discoverer.js.map +1 -0
- package/dist/github/label-manager.d.ts +44 -0
- package/dist/github/label-manager.d.ts.map +1 -0
- package/dist/github/label-manager.js +93 -0
- package/dist/github/label-manager.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/config.d.ts +4 -0
- package/dist/storage/config.d.ts.map +1 -0
- package/dist/storage/config.js +25 -0
- package/dist/storage/config.js.map +1 -0
- package/dist/storage/json-store.d.ts +20 -0
- package/dist/storage/json-store.d.ts.map +1 -0
- package/dist/storage/json-store.js +92 -0
- package/dist/storage/json-store.js.map +1 -0
- package/dist/types.d.ts +142 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +36 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright 2026 David Brophy
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Millhouse
|
|
2
|
+
|
|
3
|
+
Millhouse orchestrates parallel Claude Code instances to automatically implement work items. It supports two modes:
|
|
4
|
+
|
|
5
|
+
- **GitHub Mode:** Point it at GitHub issues and it discovers related issues, analyzes dependencies, and creates a PR with all changes
|
|
6
|
+
- **Plan Mode:** Give it any plan file (markdown, text) and it breaks it into work items automatically - no GitHub required
|
|
7
|
+
|
|
8
|
+
In both modes, Millhouse executes work items in parallel where possible, respecting dependency order.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
### Plan Mode
|
|
13
|
+
|
|
14
|
+
In Claude Code:
|
|
15
|
+
```
|
|
16
|
+
/millhouse plan # Refine your plan for millhouse
|
|
17
|
+
```
|
|
18
|
+
... then exit Claude Code and run at the command line:
|
|
19
|
+
```bash
|
|
20
|
+
millhouse run # Execute the most recent plan
|
|
21
|
+
millhouse run plan.md # Execute a specific plan file
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### GitHub Issues Mode
|
|
25
|
+
|
|
26
|
+
In Claude Code:
|
|
27
|
+
```
|
|
28
|
+
/millhouse plan # Refine your plan for millhouse
|
|
29
|
+
/millhouse issues # Create GitHub issues from your plan
|
|
30
|
+
```
|
|
31
|
+
... then exit Claude Code and run at the command line:
|
|
32
|
+
```bash
|
|
33
|
+
millhouse run issues # Run all open GitHub issues
|
|
34
|
+
millhouse run issues 5 # Run issue #5 and linked issues
|
|
35
|
+
millhouse run issues 1,2,3 # Run these issues and any linked issues
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Slash Commands
|
|
39
|
+
|
|
40
|
+
### /millhouse plan
|
|
41
|
+
|
|
42
|
+
Refines a rough plan into a millhouse-ready format. Use this before running `/millhouse issues` or `millhouse run`.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
/millhouse plan # Refine the current plan
|
|
46
|
+
/millhouse plan plan.md # Refine an existing plan file
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This transforms your plan to have:
|
|
50
|
+
- **Clearly separated work items** - each runs in a separate context
|
|
51
|
+
- **Appropriately-sized tasks** - small enough to complete unattended
|
|
52
|
+
- **Self-contained descriptions** - all context included, nothing assumed
|
|
53
|
+
- **Acceptance criteria** - how to verify each task is complete
|
|
54
|
+
- **Testing instructions** - specific commands and expected results
|
|
55
|
+
- **Explicit dependencies** - what must complete before each task
|
|
56
|
+
|
|
57
|
+
### /millhouse issues
|
|
58
|
+
|
|
59
|
+
Creates GitHub issues from a plan. Use this for GitHub mode.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
/millhouse issues # Create issues from the current plan
|
|
63
|
+
/millhouse issues plan.md # Create issues from a plan file
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## How It Works
|
|
67
|
+
|
|
68
|
+
### Dependency Analysis
|
|
69
|
+
|
|
70
|
+
When you run millhouse, it sends your work items to Claude to analyze dependencies. It understands:
|
|
71
|
+
- Explicit markers: "Depends on #1", "Blocked by #2", "After #3"
|
|
72
|
+
- Logical dependencies: If task B imports from a file that task A creates
|
|
73
|
+
- Semantic relationships: Claude infers dependencies automatically
|
|
74
|
+
|
|
75
|
+
### Parallel Execution
|
|
76
|
+
|
|
77
|
+
Work items are organized into a dependency graph. The scheduler:
|
|
78
|
+
- Starts all items with no dependencies in parallel (up to concurrency limit)
|
|
79
|
+
- As each item completes, unblocks dependent items
|
|
80
|
+
- Dynamically schedules newly-unblocked items
|
|
81
|
+
|
|
82
|
+
### Git Worktrees
|
|
83
|
+
|
|
84
|
+
Each work item runs in complete isolation:
|
|
85
|
+
- Creates a branch: `millhouse/run-{runId}-issue-{N}`
|
|
86
|
+
- Creates a git worktree in `.millhouse/worktrees/run-{runId}-issue-{N}`
|
|
87
|
+
- Claude Code runs in that worktree with full autonomy
|
|
88
|
+
- Changes are committed to the item's branch
|
|
89
|
+
- On completion, branches are merged back to the run branch
|
|
90
|
+
|
|
91
|
+
### GitHub Mode vs Plan Mode
|
|
92
|
+
|
|
93
|
+
| | GitHub Mode | Plan Mode |
|
|
94
|
+
|---|--------------------------------------------|---------------------------------|
|
|
95
|
+
| Input | GitHub issues | Any text/markdown file |
|
|
96
|
+
| Setup | `/millhouse plan` then `/millhouse issues` | `/millhouse plan` |
|
|
97
|
+
| Run | `millhouse run issues [id]` | `millhouse run [file.md]` |
|
|
98
|
+
| Output | Pull request | Changes on local branch |
|
|
99
|
+
| Labels | Auto-managed | N/A |
|
|
100
|
+
|
|
101
|
+
## CLI Reference
|
|
102
|
+
|
|
103
|
+
### Running
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Plan mode (default)
|
|
107
|
+
millhouse run # Run most recent plan from ~/.claude/plans/
|
|
108
|
+
millhouse run plan.md # Run a specific plan file
|
|
109
|
+
|
|
110
|
+
# GitHub issues mode (always discovers linked issues)
|
|
111
|
+
millhouse run issues # Run all open issues
|
|
112
|
+
millhouse run issues 5 # Run issue #5 and linked issues
|
|
113
|
+
millhouse run issues 1,2,3 # Run these issues and linked issues
|
|
114
|
+
|
|
115
|
+
# Options (work with both modes)
|
|
116
|
+
millhouse run --dry-run # Preview without executing
|
|
117
|
+
millhouse run -n 16 # Set parallel workers (default: 8)
|
|
118
|
+
millhouse run -d detailed # Start in detailed view (default: compact)
|
|
119
|
+
millhouse run --dangerously-skip-permissions # Unattended execution
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Other Commands
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
millhouse status # Show all runs
|
|
126
|
+
millhouse status --run-id X # Show specific run
|
|
127
|
+
millhouse resume <run-id> # Resume interrupted run
|
|
128
|
+
millhouse clean # Clean up leftover state
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Writing Good Plans
|
|
132
|
+
|
|
133
|
+
For best results, use `/millhouse plan` to refine your plan. Or write plans that include:
|
|
134
|
+
|
|
135
|
+
**Clear task separation:**
|
|
136
|
+
```markdown
|
|
137
|
+
## 1. Create Math Utilities
|
|
138
|
+
Create `src/math.ts` with add, subtract, multiply, divide functions.
|
|
139
|
+
|
|
140
|
+
## 2. Create Calculator Class
|
|
141
|
+
Create `src/calculator.ts` that uses the math utilities.
|
|
142
|
+
**Depends on task 1.**
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Specific implementation details:**
|
|
146
|
+
```markdown
|
|
147
|
+
Create `src/math.ts` with:
|
|
148
|
+
- `add(a: number, b: number): number`
|
|
149
|
+
- `subtract(a: number, b: number): number`
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Testing instructions:**
|
|
153
|
+
```markdown
|
|
154
|
+
## Testing
|
|
155
|
+
Run `npm test` - all tests should pass.
|
|
156
|
+
Run `npm run build` - should compile without errors.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Acceptance criteria:**
|
|
160
|
+
```markdown
|
|
161
|
+
## Acceptance Criteria
|
|
162
|
+
- [ ] All four math functions exported
|
|
163
|
+
- [ ] Tests cover edge cases (division by zero)
|
|
164
|
+
- [ ] TypeScript compiles with strict mode
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Installation
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npm install -g millhouse
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Then install the `/millhouse` slash command for Claude Code:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
millhouse setup --global
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Development Install
|
|
180
|
+
|
|
181
|
+
If you want to contribute or modify millhouse:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
git clone https://github.com/dave/millhouse.git
|
|
185
|
+
cd millhouse
|
|
186
|
+
npm install
|
|
187
|
+
npm run build
|
|
188
|
+
npm link
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
## Prerequisites
|
|
193
|
+
|
|
194
|
+
- Node.js 20+
|
|
195
|
+
- Claude Code installed and authenticated
|
|
196
|
+
- GitHub CLI (`gh`) authenticated (GitHub mode only)
|
|
197
|
+
|
|
198
|
+
## Configuration
|
|
199
|
+
|
|
200
|
+
Create `.millhouserc.json` in your project root:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"execution": {
|
|
205
|
+
"concurrency": 8,
|
|
206
|
+
"baseBranch": "main",
|
|
207
|
+
"continueOnError": true
|
|
208
|
+
},
|
|
209
|
+
"pullRequests": {
|
|
210
|
+
"createAsDraft": true,
|
|
211
|
+
"mergeStrategy": "squash"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| Option | Description | Default |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `concurrency` | Max parallel Claude instances | 8 |
|
|
219
|
+
| `baseBranch` | Branch to base work on | main |
|
|
220
|
+
| `continueOnError` | Keep going if one item fails | true |
|
|
221
|
+
| `createAsDraft` | Create PR as draft | true |
|
|
222
|
+
| `mergeStrategy` | PR merge strategy | squash |
|
|
223
|
+
|
|
224
|
+
## GitHub Labels (GitHub Mode Only)
|
|
225
|
+
|
|
226
|
+
Millhouse automatically manages labels to show progress:
|
|
227
|
+
|
|
228
|
+
| Label | Meaning |
|
|
229
|
+
|---|---|
|
|
230
|
+
| `millhouse:queued` | Waiting in queue |
|
|
231
|
+
| `millhouse:in-progress` | Claude is actively working |
|
|
232
|
+
| `millhouse:blocked` | Waiting for dependency |
|
|
233
|
+
| `millhouse:failed` | Execution failed |
|
|
234
|
+
| `millhouse:done` | Completed successfully |
|
|
235
|
+
|
|
236
|
+
## Troubleshooting
|
|
237
|
+
|
|
238
|
+
**Worktree errors after interrupted run:**
|
|
239
|
+
```bash
|
|
240
|
+
millhouse clean
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**To see what Claude is doing:**
|
|
244
|
+
The CLI shows real-time activity for each work item. Use `-d detailed` to start in detailed view, or press `v` to toggle during execution.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Millhouse
|
|
2
|
+
|
|
3
|
+
Orchestrate parallel Claude Code instances to implement work items.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Parse $ARGUMENTS to determine the subcommand:
|
|
8
|
+
|
|
9
|
+
- `/millhouse plan [plan-file]` - Refine a plan for millhouse execution
|
|
10
|
+
- `/millhouse issues [plan-file]` - Create GitHub issues from a plan
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## /millhouse plan
|
|
15
|
+
|
|
16
|
+
Refine and improve a plan to make it suitable for millhouse execution. This should be fast and unattended - don't ask clarifying questions, just improve the plan.
|
|
17
|
+
|
|
18
|
+
### Context
|
|
19
|
+
|
|
20
|
+
Millhouse executes work items in **parallel using separate Claude Code instances**, each running **unattended in its own context window**. This means:
|
|
21
|
+
|
|
22
|
+
- Each work item is a fresh start with no memory of previous work
|
|
23
|
+
- Work items must be completely self-contained
|
|
24
|
+
- Nothing can be assumed or left implicit
|
|
25
|
+
|
|
26
|
+
### Instructions
|
|
27
|
+
|
|
28
|
+
1. Read the plan from the file path argument. If no file given, look for plan.md or ask for the filename only.
|
|
29
|
+
2. Analyze the plan and rewrite it following the guidelines below
|
|
30
|
+
3. Save the improved plan to the same file (overwrite), or `plan.md` if it was a new plan
|
|
31
|
+
4. **Do not ask questions about features, scope, or implementation details** - work with what's given
|
|
32
|
+
5. **Do not suggest additions** - only restructure and clarify what's already in the plan
|
|
33
|
+
|
|
34
|
+
### Plan Improvement Guidelines
|
|
35
|
+
|
|
36
|
+
Transform the plan to be millhouse-ready:
|
|
37
|
+
|
|
38
|
+
**1. Separate work items clearly**
|
|
39
|
+
- Each work item should be a distinct section
|
|
40
|
+
- Work items will run in separate contexts, stage by stage
|
|
41
|
+
- Make boundaries between items obvious
|
|
42
|
+
|
|
43
|
+
**2. Split into appropriately-sized tasks**
|
|
44
|
+
- Break large tasks into smaller sub-tasks where necessary
|
|
45
|
+
- Each task should be small enough to complete in one go, unattended
|
|
46
|
+
- A task that's "create an entire application" is too big
|
|
47
|
+
- A task that's "create a function that adds two numbers" might be too small (unless it's a dependency)
|
|
48
|
+
|
|
49
|
+
**3. Make each task self-contained**
|
|
50
|
+
- Include ALL context needed to implement the task
|
|
51
|
+
- Specify exact file paths to create or modify
|
|
52
|
+
- Include function signatures, types, interfaces
|
|
53
|
+
- Don't assume knowledge from other tasks
|
|
54
|
+
|
|
55
|
+
**4. Add thorough acceptance criteria**
|
|
56
|
+
- How do we know this task is done?
|
|
57
|
+
- What commands verify it works? (e.g., `npm test`, `go build`)
|
|
58
|
+
- What should the output look like?
|
|
59
|
+
- What edge cases should be handled?
|
|
60
|
+
|
|
61
|
+
**5. Add testing instructions**
|
|
62
|
+
- Specific test commands to run
|
|
63
|
+
- Expected results
|
|
64
|
+
- How to verify integration with other components
|
|
65
|
+
|
|
66
|
+
**6. Make dependencies explicit**
|
|
67
|
+
- Which tasks must complete before this one can start?
|
|
68
|
+
- What does this task need from its dependencies? (files, exports, etc.)
|
|
69
|
+
- Use clear language like "This requires the math utilities from task 1"
|
|
70
|
+
|
|
71
|
+
### Important: Be Non-Interactive
|
|
72
|
+
|
|
73
|
+
- **Don't ask** if the user wants to add features, tests, or documentation
|
|
74
|
+
- **Don't suggest** expanding scope or adding "nice to haves"
|
|
75
|
+
- **Just restructure** the existing plan into millhouse-ready format
|
|
76
|
+
- If something is ambiguous, make a reasonable assumption and note it in the task
|
|
77
|
+
|
|
78
|
+
### Example Transformation
|
|
79
|
+
|
|
80
|
+
**Before (too vague):**
|
|
81
|
+
```markdown
|
|
82
|
+
# My App
|
|
83
|
+
- Build a calculator
|
|
84
|
+
- Add tests
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**After (millhouse-ready):**
|
|
88
|
+
```markdown
|
|
89
|
+
# Calculator App
|
|
90
|
+
|
|
91
|
+
## 1. Initialize TypeScript Project
|
|
92
|
+
Create the project structure with TypeScript configuration.
|
|
93
|
+
|
|
94
|
+
### Implementation
|
|
95
|
+
- Create `package.json` with name "calculator", typescript and vitest as dev dependencies
|
|
96
|
+
- Create `tsconfig.json` with strict mode, ES2020 target, NodeNext module resolution
|
|
97
|
+
- Create `src/` directory
|
|
98
|
+
|
|
99
|
+
### Testing
|
|
100
|
+
Run `npm install` and `npx tsc --noEmit` - should complete without errors.
|
|
101
|
+
|
|
102
|
+
### Acceptance Criteria
|
|
103
|
+
- [ ] `package.json` exists with correct dependencies
|
|
104
|
+
- [ ] `tsconfig.json` exists with strict mode enabled
|
|
105
|
+
- [ ] `src/` directory exists
|
|
106
|
+
- [ ] `npm install && npx tsc --noEmit` succeeds
|
|
107
|
+
|
|
108
|
+
## 2. Create Math Utilities
|
|
109
|
+
Create basic arithmetic functions used by the calculator.
|
|
110
|
+
|
|
111
|
+
**Depends on task 1** - needs the TypeScript project structure.
|
|
112
|
+
|
|
113
|
+
### Implementation
|
|
114
|
+
Create `src/math.ts` with:
|
|
115
|
+
- `add(a: number, b: number): number` - returns sum
|
|
116
|
+
- `subtract(a: number, b: number): number` - returns difference
|
|
117
|
+
- `multiply(a: number, b: number): number` - returns product
|
|
118
|
+
- `divide(a: number, b: number): number` - returns quotient, throws on division by zero
|
|
119
|
+
|
|
120
|
+
### Testing
|
|
121
|
+
Create `src/math.test.ts` with vitest tests for all functions including edge cases.
|
|
122
|
+
Run `npx vitest run` - all tests should pass.
|
|
123
|
+
|
|
124
|
+
### Acceptance Criteria
|
|
125
|
+
- [ ] `src/math.ts` exports all four functions
|
|
126
|
+
- [ ] `src/math.test.ts` has tests for each function
|
|
127
|
+
- [ ] Tests cover division by zero error case
|
|
128
|
+
- [ ] `npx vitest run` passes all tests
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## /millhouse issues
|
|
134
|
+
|
|
135
|
+
Convert a plan into GitHub issues formatted for Millhouse.
|
|
136
|
+
|
|
137
|
+
### Critical Context
|
|
138
|
+
|
|
139
|
+
These issues will be implemented by Claude Code instances running **unattended in separate context windows**. Each issue is a fresh start with no memory of previous conversations. This means:
|
|
140
|
+
|
|
141
|
+
- Every issue must be completely self-contained
|
|
142
|
+
- All relevant context, requirements, and constraints must be explicitly stated
|
|
143
|
+
- Nothing can be assumed or left implicit
|
|
144
|
+
|
|
145
|
+
### Instructions
|
|
146
|
+
|
|
147
|
+
1. Read the plan from the file path argument, or ask the user to describe it
|
|
148
|
+
2. Break it into discrete, implementable issues
|
|
149
|
+
3. For each issue, identify dependencies on other issues
|
|
150
|
+
4. Create issues in order using `gh issue create`, starting with issues that have no dependencies
|
|
151
|
+
5. Use the actual issue numbers returned by GitHub for dependency references
|
|
152
|
+
6. After creating all issues, create an **index issue** that lists them all (see below)
|
|
153
|
+
|
|
154
|
+
### Issue Content Requirements
|
|
155
|
+
|
|
156
|
+
Each issue MUST include:
|
|
157
|
+
|
|
158
|
+
**Implementation Details**
|
|
159
|
+
- Specific file paths to create or modify
|
|
160
|
+
- Function signatures, types, or interfaces expected
|
|
161
|
+
- Any specific libraries or patterns to use
|
|
162
|
+
|
|
163
|
+
**Testing & Verification**
|
|
164
|
+
- How to verify the implementation works
|
|
165
|
+
- Specific test commands to run (e.g., `npm test`, `npm run build`)
|
|
166
|
+
- Expected output or behavior
|
|
167
|
+
|
|
168
|
+
**Acceptance Criteria**
|
|
169
|
+
- Clear, checkable criteria for when the issue is "done"
|
|
170
|
+
- Edge cases to handle
|
|
171
|
+
- Error conditions to consider
|
|
172
|
+
|
|
173
|
+
**Dependencies**
|
|
174
|
+
- `**Depends on #N**` if it depends on another issue
|
|
175
|
+
- What specifically it needs from that dependency (files, exports, etc.)
|
|
176
|
+
|
|
177
|
+
### Example Issue Body
|
|
178
|
+
|
|
179
|
+
```markdown
|
|
180
|
+
Create `src/utils/math.ts` with basic arithmetic functions.
|
|
181
|
+
|
|
182
|
+
## Implementation
|
|
183
|
+
- Export functions: `add(a: number, b: number): number` and `multiply(a: number, b: number): number`
|
|
184
|
+
- Use ES module syntax (export, not module.exports)
|
|
185
|
+
- No external dependencies
|
|
186
|
+
|
|
187
|
+
## Testing
|
|
188
|
+
Run `npx tsc --noEmit` to verify no type errors.
|
|
189
|
+
|
|
190
|
+
## Acceptance Criteria
|
|
191
|
+
- [ ] File exists at `src/utils/math.ts`
|
|
192
|
+
- [ ] Both functions are exported
|
|
193
|
+
- [ ] TypeScript compiles without errors
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Index Issue
|
|
197
|
+
|
|
198
|
+
After creating all implementation issues, create a final **index issue** that serves as an overview. Title it something like "Implement [feature name]" and include:
|
|
199
|
+
|
|
200
|
+
- A one-line summary of the overall goal
|
|
201
|
+
- A list of all created issues with their numbers and a brief description
|
|
202
|
+
- The command to run: `millhouse run issues <index-number>`
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
|
|
206
|
+
```markdown
|
|
207
|
+
Implement a calculator library with math utilities.
|
|
208
|
+
|
|
209
|
+
## Issues
|
|
210
|
+
|
|
211
|
+
- #1 Create math utilities (`src/utils/math.ts`)
|
|
212
|
+
- #2 Create string helpers (`src/utils/string.ts`)
|
|
213
|
+
- #3 Create calculator class (`src/calculator.ts`)
|
|
214
|
+
- #4 Create main entry point (`src/index.ts`)
|
|
215
|
+
|
|
216
|
+
## Run
|
|
217
|
+
|
|
218
|
+
\`\`\`bash
|
|
219
|
+
millhouse run issues <this-issue-number>
|
|
220
|
+
\`\`\`
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The index issue should **not** include detailed dependency information—Millhouse will discover dependencies from the individual issues.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AnalyzedIssue } from '../types.js';
|
|
2
|
+
export interface DependencyGraph {
|
|
3
|
+
/**
|
|
4
|
+
* Get all issue numbers that this issue depends on.
|
|
5
|
+
*/
|
|
6
|
+
getDependencies(issueNumber: number): number[];
|
|
7
|
+
/**
|
|
8
|
+
* Get all issue numbers that depend on this issue.
|
|
9
|
+
*/
|
|
10
|
+
getDependents(issueNumber: number): number[];
|
|
11
|
+
/**
|
|
12
|
+
* Get issues that are ready to start (all dependencies completed).
|
|
13
|
+
*/
|
|
14
|
+
getReady(completed: number[]): number[];
|
|
15
|
+
/**
|
|
16
|
+
* Get issues that are blocked (have uncompleted dependencies).
|
|
17
|
+
*/
|
|
18
|
+
getBlocked(completed: number[]): number[];
|
|
19
|
+
/**
|
|
20
|
+
* Get topological order of issues.
|
|
21
|
+
*/
|
|
22
|
+
getTopologicalOrder(): number[];
|
|
23
|
+
/**
|
|
24
|
+
* Check if the graph is a valid DAG (no cycles).
|
|
25
|
+
*/
|
|
26
|
+
isAcyclic(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get cycles if any exist.
|
|
29
|
+
*/
|
|
30
|
+
getCycles(): number[][];
|
|
31
|
+
/**
|
|
32
|
+
* Get all issue numbers in the graph.
|
|
33
|
+
*/
|
|
34
|
+
getAllIssues(): number[];
|
|
35
|
+
}
|
|
36
|
+
export declare class GraphBuilder {
|
|
37
|
+
/**
|
|
38
|
+
* Build a dependency graph from analyzed issues.
|
|
39
|
+
*/
|
|
40
|
+
build(issues: AnalyzedIssue[]): DependencyGraph;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=graph-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-builder.d.ts","sourceRoot":"","sources":["../../src/analysis/graph-builder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE/C;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE7C;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE1C;;OAEG;IACH,mBAAmB,IAAI,MAAM,EAAE,CAAC;IAEhC;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC;IAErB;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE,EAAE,CAAC;IAExB;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE,CAAC;CAC1B;AAED,qBAAa,YAAY;IACvB;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,eAAe;CAoBhD"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import graphlib from 'graphlib';
|
|
2
|
+
const { Graph, alg } = graphlib;
|
|
3
|
+
export class GraphBuilder {
|
|
4
|
+
/**
|
|
5
|
+
* Build a dependency graph from analyzed issues.
|
|
6
|
+
*/
|
|
7
|
+
build(issues) {
|
|
8
|
+
const graph = new Graph({ directed: true });
|
|
9
|
+
// Add all nodes
|
|
10
|
+
for (const issue of issues) {
|
|
11
|
+
graph.setNode(String(issue.number), issue);
|
|
12
|
+
}
|
|
13
|
+
// Add edges (dependency -> dependent)
|
|
14
|
+
for (const issue of issues) {
|
|
15
|
+
for (const dep of issue.dependencies) {
|
|
16
|
+
if (graph.hasNode(String(dep))) {
|
|
17
|
+
// Edge from dependency to dependent (dep must complete before issue)
|
|
18
|
+
graph.setEdge(String(dep), String(issue.number));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return new DependencyGraphImpl(graph);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class DependencyGraphImpl {
|
|
26
|
+
graph;
|
|
27
|
+
constructor(graph) {
|
|
28
|
+
this.graph = graph;
|
|
29
|
+
}
|
|
30
|
+
getDependencies(issueNumber) {
|
|
31
|
+
const predecessors = this.graph.predecessors(String(issueNumber));
|
|
32
|
+
if (!predecessors)
|
|
33
|
+
return [];
|
|
34
|
+
return predecessors.map((n) => parseInt(n, 10));
|
|
35
|
+
}
|
|
36
|
+
getDependents(issueNumber) {
|
|
37
|
+
const successors = this.graph.successors(String(issueNumber));
|
|
38
|
+
if (!successors)
|
|
39
|
+
return [];
|
|
40
|
+
return successors.map((n) => parseInt(n, 10));
|
|
41
|
+
}
|
|
42
|
+
getReady(completed) {
|
|
43
|
+
const completedSet = new Set(completed.map(String));
|
|
44
|
+
const ready = [];
|
|
45
|
+
for (const node of this.graph.nodes()) {
|
|
46
|
+
// Skip if already completed
|
|
47
|
+
if (completedSet.has(node))
|
|
48
|
+
continue;
|
|
49
|
+
// Check if all dependencies are completed
|
|
50
|
+
const deps = this.graph.predecessors(node) || [];
|
|
51
|
+
const allDepsCompleted = deps.every((d) => completedSet.has(d));
|
|
52
|
+
if (allDepsCompleted) {
|
|
53
|
+
ready.push(parseInt(node, 10));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return ready;
|
|
57
|
+
}
|
|
58
|
+
getBlocked(completed) {
|
|
59
|
+
const completedSet = new Set(completed.map(String));
|
|
60
|
+
const blocked = [];
|
|
61
|
+
for (const node of this.graph.nodes()) {
|
|
62
|
+
// Skip if already completed
|
|
63
|
+
if (completedSet.has(node))
|
|
64
|
+
continue;
|
|
65
|
+
// Check if any dependency is not completed
|
|
66
|
+
const deps = this.graph.predecessors(node) || [];
|
|
67
|
+
const hasPendingDeps = deps.some((d) => !completedSet.has(d));
|
|
68
|
+
if (hasPendingDeps) {
|
|
69
|
+
blocked.push(parseInt(node, 10));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return blocked;
|
|
73
|
+
}
|
|
74
|
+
getTopologicalOrder() {
|
|
75
|
+
try {
|
|
76
|
+
const sorted = alg.topsort(this.graph);
|
|
77
|
+
return sorted.map((n) => parseInt(n, 10));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Graph has cycles, return empty
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
isAcyclic() {
|
|
85
|
+
return alg.isAcyclic(this.graph);
|
|
86
|
+
}
|
|
87
|
+
getCycles() {
|
|
88
|
+
const tarjan = alg.tarjan(this.graph);
|
|
89
|
+
// tarjan returns SCCs; cycles are SCCs with more than 1 node
|
|
90
|
+
return tarjan
|
|
91
|
+
.filter((scc) => scc.length > 1)
|
|
92
|
+
.map((scc) => scc.map((n) => parseInt(n, 10)));
|
|
93
|
+
}
|
|
94
|
+
getAllIssues() {
|
|
95
|
+
return this.graph.nodes().map((n) => parseInt(n, 10));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=graph-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-builder.js","sourceRoot":"","sources":["../../src/analysis/graph-builder.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAIhC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC;AA4ChC,MAAM,OAAO,YAAY;IACvB;;OAEG;IACH,KAAK,CAAC,MAAuB;QAC3B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5C,gBAAgB;QAChB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;QAED,sCAAsC;QACtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;gBACrC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBAC/B,qEAAqE;oBACrE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;CACF;AAED,MAAM,mBAAmB;IACH;IAApB,YAAoB,KAAgB;QAAhB,UAAK,GAAL,KAAK,CAAW;IAAG,CAAC;IAExC,eAAe,CAAC,WAAmB;QACjC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,YAAY;YAAE,OAAO,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,aAAa,CAAC,WAAmB;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QAC3B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,QAAQ,CAAC,SAAmB;QAC1B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACpD,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACtC,4BAA4B;YAC5B,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAErC,0CAA0C;YAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACjD,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAExE,IAAI,gBAAgB,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,SAAmB;QAC5B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACpD,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACtC,4BAA4B;YAC5B,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAErC,2CAA2C;YAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACjD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAEtE,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,mBAAmB;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,SAAS;QACP,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,SAAS;QACP,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,6DAA6D;QAC7D,OAAO,MAAM;aACV,MAAM,CAAC,CAAC,GAAa,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;aACzC,GAAG,CAAC,CAAC,GAAa,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { GitHubIssue, AnalyzedIssue } from '../types.js';
|
|
2
|
+
export declare class IssueAnalyzer {
|
|
3
|
+
/**
|
|
4
|
+
* Analyze all issues in a single Claude call to determine dependencies.
|
|
5
|
+
*/
|
|
6
|
+
analyzeIssues(issues: GitHubIssue[]): Promise<AnalyzedIssue[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Fallback to pattern matching if Claude analysis fails.
|
|
9
|
+
*/
|
|
10
|
+
private fallbackAnalysis;
|
|
11
|
+
/**
|
|
12
|
+
* Extract dependencies using pattern matching.
|
|
13
|
+
*/
|
|
14
|
+
private extractDependencies;
|
|
15
|
+
/**
|
|
16
|
+
* Extract affected file paths from issue body.
|
|
17
|
+
*/
|
|
18
|
+
private extractAffectedPaths;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=issue-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-analyzer.d.ts","sourceRoot":"","sources":["../../src/analysis/issue-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqC9D,qBAAa,aAAa;IACxB;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAuFpE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA8B3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAa7B"}
|