@graypark/loophaus 3.4.0 → 3.5.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/README.ko.md +81 -17
- package/README.md +69 -15
- package/dist/.claude-plugin/plugin.json +11 -0
- package/dist/LICENSE +21 -0
- package/dist/README.ko.md +422 -0
- package/dist/README.md +336 -0
- package/dist/bin/install.d.ts +3 -0
- package/dist/bin/install.d.ts.map +1 -0
- package/{bin/install.mjs → dist/bin/install.js} +3 -5
- package/dist/bin/install.js.map +1 -0
- package/dist/bin/loophaus.d.ts +3 -0
- package/dist/bin/loophaus.d.ts.map +1 -0
- package/dist/bin/loophaus.js +654 -0
- package/dist/bin/loophaus.js.map +1 -0
- package/dist/bin/uninstall.d.ts +8 -0
- package/dist/bin/uninstall.d.ts.map +1 -0
- package/dist/bin/uninstall.js +209 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/codex/commands/cancel-ralph.md +30 -0
- package/dist/codex/commands/ralph-loop.md +73 -0
- package/dist/commands/cancel-ralph.md +23 -0
- package/dist/commands/help.md +96 -0
- package/dist/commands/loop-plan.md +257 -0
- package/dist/commands/loop-pulse.md +38 -0
- package/dist/commands/loop-stop.md +29 -0
- package/dist/commands/loop.md +17 -0
- package/dist/commands/ralph-loop.md +18 -0
- package/dist/core/cost-tracker.d.ts +33 -0
- package/dist/core/cost-tracker.d.ts.map +1 -0
- package/dist/core/cost-tracker.js +41 -0
- package/dist/core/cost-tracker.js.map +1 -0
- package/dist/core/engine.d.ts +4 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +109 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/event-logger.d.ts +5 -0
- package/dist/core/event-logger.d.ts.map +1 -0
- package/dist/core/event-logger.js +48 -0
- package/dist/core/event-logger.js.map +1 -0
- package/dist/core/events.d.ts +34 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +44 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/io-helpers.d.ts +3 -0
- package/dist/core/io-helpers.d.ts.map +1 -0
- package/dist/core/io-helpers.js +65 -0
- package/dist/core/io-helpers.js.map +1 -0
- package/dist/core/loop-registry.d.ts +10 -0
- package/dist/core/loop-registry.d.ts.map +1 -0
- package/dist/core/loop-registry.js +37 -0
- package/dist/core/loop-registry.js.map +1 -0
- package/dist/core/merge-strategy.d.ts +7 -0
- package/dist/core/merge-strategy.d.ts.map +1 -0
- package/dist/core/merge-strategy.js +82 -0
- package/dist/core/merge-strategy.js.map +1 -0
- package/dist/core/parallel-runner.d.ts +32 -0
- package/dist/core/parallel-runner.d.ts.map +1 -0
- package/dist/core/parallel-runner.js +88 -0
- package/dist/core/parallel-runner.js.map +1 -0
- package/dist/core/policy.d.ts +22 -0
- package/dist/core/policy.d.ts.map +1 -0
- package/dist/core/policy.js +54 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/quality-scorer.d.ts +40 -0
- package/dist/core/quality-scorer.d.ts.map +1 -0
- package/dist/core/quality-scorer.js +128 -0
- package/dist/core/quality-scorer.js.map +1 -0
- package/dist/core/refine-loop.d.ts +16 -0
- package/dist/core/refine-loop.d.ts.map +1 -0
- package/dist/core/refine-loop.js +26 -0
- package/dist/core/refine-loop.js.map +1 -0
- package/dist/core/session.d.ts +27 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +67 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/trace-analyzer.d.ts +28 -0
- package/dist/core/trace-analyzer.d.ts.map +1 -0
- package/dist/core/trace-analyzer.js +46 -0
- package/dist/core/trace-analyzer.js.map +1 -0
- package/dist/core/types.d.ts +99 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validate.d.ts +7 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +55 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/core/worktree.d.ts +13 -0
- package/dist/core/worktree.d.ts.map +1 -0
- package/dist/core/worktree.js +108 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/hooks/hooks.json +15 -0
- package/dist/hooks/stop-hook.mjs +111 -0
- package/dist/lib/paths.d.ts +18 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +74 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/stop-hook-core.d.ts +19 -0
- package/dist/lib/stop-hook-core.d.ts.map +1 -0
- package/dist/lib/stop-hook-core.js +36 -0
- package/dist/lib/stop-hook-core.js.map +1 -0
- package/dist/package.json +61 -0
- package/dist/platforms/claude-code/adapter.mjs +20 -0
- package/dist/platforms/claude-code/installer.d.mts +3 -0
- package/dist/platforms/claude-code/installer.mjs +173 -0
- package/dist/platforms/codex-cli/adapter.mjs +20 -0
- package/dist/platforms/codex-cli/installer.d.mts +2 -0
- package/dist/platforms/codex-cli/installer.mjs +247 -0
- package/dist/platforms/kiro-cli/adapter.mjs +21 -0
- package/dist/platforms/kiro-cli/installer.d.mts +3 -0
- package/dist/platforms/kiro-cli/installer.mjs +257 -0
- package/dist/scripts/setup-ralph-loop.sh +145 -0
- package/dist/skills/ralph-claude-cancel/SKILL.md +23 -0
- package/dist/skills/ralph-claude-interview/SKILL.md +184 -0
- package/dist/skills/ralph-claude-loop/SKILL.md +101 -0
- package/dist/skills/ralph-claude-orchestrator/SKILL.md +129 -0
- package/dist/skills/ralph-interview/SKILL.md +275 -0
- package/dist/skills/ralph-orchestrator/SKILL.md +254 -0
- package/dist/store/state-store.d.ts +17 -0
- package/dist/store/state-store.d.ts.map +1 -0
- package/dist/store/state-store.js +108 -0
- package/dist/store/state-store.js.map +1 -0
- package/hooks/stop-hook.mjs +6 -6
- package/package.json +11 -7
- package/platforms/claude-code/installer.d.mts +3 -0
- package/platforms/claude-code/installer.mjs +2 -2
- package/platforms/codex-cli/installer.d.mts +2 -0
- package/platforms/codex-cli/installer.mjs +1 -1
- package/platforms/kiro-cli/installer.d.mts +3 -0
- package/bin/loophaus.mjs +0 -521
- package/bin/uninstall.mjs +0 -255
- package/core/cost-tracker.mjs +0 -44
- package/core/engine.mjs +0 -123
- package/core/event-logger.mjs +0 -37
- package/core/events.mjs +0 -48
- package/core/io-helpers.mjs +0 -33
- package/core/loop-registry.mjs +0 -37
- package/core/loop.schema.json +0 -29
- package/core/merge-strategy.mjs +0 -72
- package/core/parallel-runner.mjs +0 -94
- package/core/policy.mjs +0 -58
- package/core/quality-scorer.mjs +0 -136
- package/core/refine-loop.mjs +0 -29
- package/core/session.mjs +0 -66
- package/core/state.schema.json +0 -24
- package/core/trace-analyzer.mjs +0 -51
- package/core/validate.mjs +0 -54
- package/core/worktree.mjs +0 -97
- package/lib/paths.mjs +0 -99
- package/lib/stop-hook-core.mjs +0 -42
- package/store/state-store.mjs +0 -106
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralph-orchestrator
|
|
3
|
+
description: "Orchestration patterns for loop: subagent spawning, parallel exploration, and task decomposition strategies"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Loop Orchestrator — Multi-Agent Loop Patterns
|
|
7
|
+
|
|
8
|
+
You are an expert at designing optimal execution strategies for Loop tasks.
|
|
9
|
+
When asked to orchestrate a task, analyze its structure and recommend the best combination of sequential loops, parallel subagents, and phased execution.
|
|
10
|
+
|
|
11
|
+
## Core Concept
|
|
12
|
+
|
|
13
|
+
Not every task should run in a single linear loop. Complex tasks benefit from:
|
|
14
|
+
|
|
15
|
+
- **Parallel exploration** — spawn subagents to search/analyze independently, then merge results
|
|
16
|
+
- **Divide and conquer** — split work by service/directory/concern with isolated agents
|
|
17
|
+
- **Pipeline stages** — sequential phases where each feeds into the next
|
|
18
|
+
- **Hybrid patterns** — combine parallel research with sequential implementation
|
|
19
|
+
|
|
20
|
+
## When to Use Subagents
|
|
21
|
+
|
|
22
|
+
### Use Subagents (Parallel) When:
|
|
23
|
+
|
|
24
|
+
| Signal | Example |
|
|
25
|
+
| --------------------------- | ---------------------------------------------------------- |
|
|
26
|
+
| **Broad codebase search** | "Find all API endpoints that lack auth checks" |
|
|
27
|
+
| **Independent file groups** | Frontend components + backend routes + database migrations |
|
|
28
|
+
| **Multi-service changes** | auth-service + api-gateway + frontend simultaneously |
|
|
29
|
+
| **Audit/review tasks** | Security audit + performance audit + accessibility audit |
|
|
30
|
+
| **Pattern extraction** | Scan 50+ files for inconsistent patterns |
|
|
31
|
+
|
|
32
|
+
### Do NOT Use Subagents When:
|
|
33
|
+
|
|
34
|
+
| Signal | Reason |
|
|
35
|
+
| --------------------------- | ---------------------------------------------------- |
|
|
36
|
+
| **Sequential dependencies** | Step 2 needs Step 1's output |
|
|
37
|
+
| **Shared mutable state** | Multiple agents editing the same file = conflicts |
|
|
38
|
+
| **Small scope (< 5 files)** | Overhead outweighs benefit |
|
|
39
|
+
| **Context-heavy tasks** | Agent needs deep understanding of full codebase flow |
|
|
40
|
+
|
|
41
|
+
## Orchestration Patterns
|
|
42
|
+
|
|
43
|
+
### Pattern 1: Parallel Explore → Sequential Implement
|
|
44
|
+
|
|
45
|
+
Best for: Large codebases where you need to understand before you change.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Phase 1 (Parallel Subagents):
|
|
49
|
+
├── Agent A: Scan src/frontend/** for pattern X → report-frontend.md
|
|
50
|
+
├── Agent B: Scan src/backend/** for pattern X → report-backend.md
|
|
51
|
+
└── Agent C: Scan src/shared/** for pattern X → report-shared.md
|
|
52
|
+
|
|
53
|
+
Phase 2 (Sequential Loop):
|
|
54
|
+
└── Loop: Read all reports → implement fixes in priority order
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Prompt pattern for Phase 1:**
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
## Subagent Tasks (run in parallel)
|
|
61
|
+
Use the Agent tool to spawn these subagents simultaneously:
|
|
62
|
+
|
|
63
|
+
1. Agent "frontend-scan": Search src/frontend/** for [pattern].
|
|
64
|
+
Write findings to .loophaus/reports/frontend-scan.md
|
|
65
|
+
2. Agent "backend-scan": Search src/backend/** for [pattern].
|
|
66
|
+
Write findings to .loophaus/reports/backend-scan.md
|
|
67
|
+
3. Agent "shared-scan": Search src/shared/** for [pattern].
|
|
68
|
+
Write findings to .loophaus/reports/shared-scan.md
|
|
69
|
+
|
|
70
|
+
After ALL agents complete, merge reports into .loophaus/reports/merged.md
|
|
71
|
+
with a unified priority list.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Pattern 2: Divide by Ownership
|
|
75
|
+
|
|
76
|
+
Best for: Multi-service changes where files don't overlap.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Phase 1 (Parallel Implementation):
|
|
80
|
+
├── Agent "fe-dev": Modify src/frontend/** only → commit per item
|
|
81
|
+
├── Agent "be-dev": Modify src/backend/** only → commit per item
|
|
82
|
+
└── Agent "auth-dev": Modify src/auth/** only → commit per item
|
|
83
|
+
|
|
84
|
+
Phase 2 (Integration Verification):
|
|
85
|
+
└── Loop: Run full test suite, fix any integration issues
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Prompt pattern:**
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
## Parallel Work Streams
|
|
92
|
+
Spawn these agents with strict file ownership:
|
|
93
|
+
|
|
94
|
+
1. Agent "fe-dev" (isolation: worktree):
|
|
95
|
+
- ONLY touch files in src/frontend/**
|
|
96
|
+
- Tasks: [frontend items]
|
|
97
|
+
- Commit each fix individually
|
|
98
|
+
|
|
99
|
+
2. Agent "be-dev" (isolation: worktree):
|
|
100
|
+
- ONLY touch files in src/backend/**
|
|
101
|
+
- Tasks: [backend items]
|
|
102
|
+
- Commit each fix individually
|
|
103
|
+
|
|
104
|
+
After all agents complete, merge their branches and run integration tests.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Pattern 3: Fan-Out / Fan-In Research
|
|
108
|
+
|
|
109
|
+
Best for: Tasks that need comprehensive analysis before any action.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Fan-Out (Parallel):
|
|
113
|
+
├── Agent 1: Analyze problem from angle A → findings-a.md
|
|
114
|
+
├── Agent 2: Analyze problem from angle B → findings-b.md
|
|
115
|
+
└── Agent 3: Analyze problem from angle C → findings-c.md
|
|
116
|
+
|
|
117
|
+
Fan-In (Sequential):
|
|
118
|
+
└── Loop: Synthesize all findings → create action plan → implement
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Pattern 4: Pipeline with Checkpoints
|
|
122
|
+
|
|
123
|
+
Best for: Complex multi-stage transformations.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Stage 1 (Loop): Parse + validate input → intermediate.json
|
|
127
|
+
↓ checkpoint: verify intermediate.json schema
|
|
128
|
+
Stage 2 (Loop): Transform data → output draft
|
|
129
|
+
↓ checkpoint: run regression tests
|
|
130
|
+
Stage 3 (Parallel Agents): Apply fixes to multiple output files
|
|
131
|
+
↓ checkpoint: final integration test
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Pattern 5: Scout-Then-Execute
|
|
135
|
+
|
|
136
|
+
Best for: Unfamiliar codebases or risky changes.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Scout Phase (Single Agent, read-only):
|
|
140
|
+
└── Agent "scout": Explore codebase, map dependencies, identify risks
|
|
141
|
+
→ .loophaus/reports/scout-report.md
|
|
142
|
+
|
|
143
|
+
Execute Phase (Loop):
|
|
144
|
+
└── Loop: Use scout report as reference, implement changes
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Subagent Configuration Reference
|
|
148
|
+
|
|
149
|
+
### Codex CLI (experimental)
|
|
150
|
+
|
|
151
|
+
Codex spawns subagents via natural language prompts with keywords: "spawn", "parallel", "delegate", "one agent per".
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
# Enable in ~/.codex/config.toml
|
|
155
|
+
[agents]
|
|
156
|
+
max_threads = 6 # Max concurrent agent threads (default: 6)
|
|
157
|
+
max_depth = 1 # No grandchild agents (default: 1)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Custom agent definitions** in `.codex/agents/` (TOML):
|
|
161
|
+
|
|
162
|
+
```toml
|
|
163
|
+
# .codex/agents/scanner.toml
|
|
164
|
+
name = "scanner"
|
|
165
|
+
description = "Read-only codebase explorer for pattern extraction"
|
|
166
|
+
model = "gpt-5.4-mini" # Faster model for exploration
|
|
167
|
+
model_reasoning_effort = "low"
|
|
168
|
+
sandbox_mode = "read-only"
|
|
169
|
+
developer_instructions = """
|
|
170
|
+
Scan files for the requested pattern. Report findings with file paths and line numbers.
|
|
171
|
+
Do NOT modify any files.
|
|
172
|
+
"""
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Spawning in prompts:**
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
"Spawn one agent per service directory to scan for auth issues:
|
|
179
|
+
1. scanner on src/frontend/** → .loophaus/reports/frontend.md
|
|
180
|
+
2. scanner on src/backend/** → .loophaus/reports/backend.md
|
|
181
|
+
3. scanner on src/auth/** → .loophaus/reports/auth.md
|
|
182
|
+
Wait for all, then summarize findings by severity."
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Batch processing** with `spawn_agents_on_csv`:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
# For 100+ similar items, use CSV-driven batch spawning:
|
|
189
|
+
spawn_agents_on_csv:
|
|
190
|
+
csv_path: .loophaus/items.csv
|
|
191
|
+
instruction: "Review {file_path} for {issue_type}. Return JSON via report_agent_job_result"
|
|
192
|
+
output_schema: { file: string, severity: string, fix: string }
|
|
193
|
+
output_csv_path: .loophaus/reports/batch-results.csv
|
|
194
|
+
max_concurrency: 6
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Model Selection Guide (Codex)
|
|
198
|
+
|
|
199
|
+
| Role | Recommended Model | Reasoning Effort |
|
|
200
|
+
| ----------------------- | ------------------- | ---------------- |
|
|
201
|
+
| Coordinator / main loop | gpt-5.4 | medium |
|
|
202
|
+
| Explorer / scanner | gpt-5.4-mini | low |
|
|
203
|
+
| Reviewer / security | gpt-5.4 | high |
|
|
204
|
+
| Quick iteration / TDD | gpt-5.3-codex-spark | medium |
|
|
205
|
+
|
|
206
|
+
### Key Constraints
|
|
207
|
+
|
|
208
|
+
| Setting | Default | Note |
|
|
209
|
+
| -------------------- | ------- | ----------------------------------------------------------------------- |
|
|
210
|
+
| `agents.max_threads` | 6 | Hard cap on concurrent agents |
|
|
211
|
+
| `agents.max_depth` | 1 | No recursive agent spawning |
|
|
212
|
+
| File conflicts | N/A | Multiple agents writing same file = conflicts. Use ownership isolation. |
|
|
213
|
+
| Token usage | Higher | Each subagent does own inference. Use faster models for workers. |
|
|
214
|
+
|
|
215
|
+
## Report Directory Convention
|
|
216
|
+
|
|
217
|
+
All subagent outputs should follow this structure:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
.loophaus/
|
|
221
|
+
└── reports/
|
|
222
|
+
├── {agent-name}.md # Individual agent output
|
|
223
|
+
├── merged.md # Combined findings (created by orchestrator)
|
|
224
|
+
└── plan.md # Action plan derived from findings
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This directory should be in `.gitignore` — reports are ephemeral working artifacts.
|
|
228
|
+
|
|
229
|
+
## Decision Matrix
|
|
230
|
+
|
|
231
|
+
When analyzing a task, use this scoring to decide the orchestration pattern:
|
|
232
|
+
|
|
233
|
+
| Factor | Score | Pattern |
|
|
234
|
+
| ------------------------------ | ----- | ------------------- |
|
|
235
|
+
| Files span 3+ directories | +2 | Parallel |
|
|
236
|
+
| Items are independent | +2 | Parallel |
|
|
237
|
+
| Need full context to decide | -2 | Sequential |
|
|
238
|
+
| Order matters | -2 | Sequential |
|
|
239
|
+
| 10+ similar items | +1 | Parallel |
|
|
240
|
+
| Needs cross-file understanding | -1 | Sequential |
|
|
241
|
+
| Multiple services/repos | +3 | Divide by Ownership |
|
|
242
|
+
|
|
243
|
+
- **Score >= 3**: Recommend parallel subagents
|
|
244
|
+
- **Score 0–2**: Recommend sequential with optional scout phase
|
|
245
|
+
- **Score < 0**: Recommend single sequential Loop
|
|
246
|
+
|
|
247
|
+
## Integration with Loop Interview
|
|
248
|
+
|
|
249
|
+
When `/loop-plan` invokes this skill, provide:
|
|
250
|
+
|
|
251
|
+
1. **Recommended pattern** — which orchestration pattern fits best
|
|
252
|
+
2. **Agent breakdown** — list of subagents with their roles and file boundaries
|
|
253
|
+
3. **Phase structure** — how phases connect and what checkpoints exist
|
|
254
|
+
4. **Prompt snippets** — ready-to-embed subagent instructions for the loop prompt
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LoopState, QualityConfig } from "../core/types.js";
|
|
2
|
+
export interface StateStoreData extends LoopState {
|
|
3
|
+
qualityThreshold: number;
|
|
4
|
+
maxRefineAttempts: number;
|
|
5
|
+
qualityConfig: QualityConfig | null;
|
|
6
|
+
}
|
|
7
|
+
declare const DEFAULT_STATE: StateStoreData;
|
|
8
|
+
export declare function getStatePath(cwd?: string, name?: string): string;
|
|
9
|
+
export declare function read(cwd?: string, name?: string): Promise<StateStoreData>;
|
|
10
|
+
export declare function write(state: StateStoreData, cwd?: string, name?: string): Promise<void>;
|
|
11
|
+
export declare function reset(cwd?: string): Promise<void>;
|
|
12
|
+
export declare function readState(cwd?: string): Promise<StateStoreData>;
|
|
13
|
+
export declare function writeState(state: StateStoreData, cwd?: string): Promise<void>;
|
|
14
|
+
export declare function resetState(cwd?: string): Promise<void>;
|
|
15
|
+
export declare function incrementIteration(cwd?: string): Promise<StateStoreData>;
|
|
16
|
+
export { DEFAULT_STATE };
|
|
17
|
+
//# sourceMappingURL=state-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../store/state-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjE,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,aAAa,EAAE,cAWpB,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhE;AAOD,wBAAsB,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA8B/E;AAED,wBAAsB,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7F;AAED,wBAAsB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvD;AAoBD,wBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAsB;AAC5F,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAA8B;AAClH,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAuB;AACpF,wBAAsB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAK9E;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { validateState } from "../core/validate.js";
|
|
4
|
+
const DEFAULT_STATE = {
|
|
5
|
+
active: false,
|
|
6
|
+
prompt: "",
|
|
7
|
+
completionPromise: "TADA",
|
|
8
|
+
maxIterations: 20,
|
|
9
|
+
currentIteration: 0,
|
|
10
|
+
sessionId: "",
|
|
11
|
+
verifyScript: "",
|
|
12
|
+
qualityThreshold: 80,
|
|
13
|
+
maxRefineAttempts: 3,
|
|
14
|
+
qualityConfig: null,
|
|
15
|
+
};
|
|
16
|
+
export function getStatePath(cwd, name) {
|
|
17
|
+
if (process.env.LOOPHAUS_STATE_FILE)
|
|
18
|
+
return process.env.LOOPHAUS_STATE_FILE;
|
|
19
|
+
if (process.env.RALPH_STATE_FILE)
|
|
20
|
+
return process.env.RALPH_STATE_FILE;
|
|
21
|
+
const base = cwd || process.cwd();
|
|
22
|
+
if (name)
|
|
23
|
+
return join(base, ".loophaus", "loops", name, "state.json");
|
|
24
|
+
return join(base, ".loophaus", "state.json");
|
|
25
|
+
}
|
|
26
|
+
const LEGACY_PATHS = [
|
|
27
|
+
(cwd) => join(cwd, ".codex", "ralph-loop.state.json"),
|
|
28
|
+
(cwd) => join(cwd, ".claude", "ralph-loop.local.md"),
|
|
29
|
+
];
|
|
30
|
+
export async function read(cwd, name) {
|
|
31
|
+
const primary = getStatePath(cwd, name);
|
|
32
|
+
try {
|
|
33
|
+
const raw = await readFile(primary, "utf-8");
|
|
34
|
+
const state = { ...DEFAULT_STATE, ...JSON.parse(raw) };
|
|
35
|
+
const result = validateState(state);
|
|
36
|
+
if (!result.valid) {
|
|
37
|
+
process.stderr.write(`loophaus: state validation warning: ${result.errors.join(", ")}\n`);
|
|
38
|
+
}
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Primary not found, try legacy paths
|
|
43
|
+
}
|
|
44
|
+
const dir = cwd || process.cwd();
|
|
45
|
+
for (const pathFn of LEGACY_PATHS) {
|
|
46
|
+
const legacyPath = pathFn(dir);
|
|
47
|
+
try {
|
|
48
|
+
const raw = await readFile(legacyPath, "utf-8");
|
|
49
|
+
if (legacyPath.endsWith(".md")) {
|
|
50
|
+
return migrateMdFormat(raw);
|
|
51
|
+
}
|
|
52
|
+
return { ...DEFAULT_STATE, ...JSON.parse(raw) };
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { ...DEFAULT_STATE };
|
|
59
|
+
}
|
|
60
|
+
export async function write(state, cwd, name) {
|
|
61
|
+
const result = validateState(state);
|
|
62
|
+
if (!result.valid) {
|
|
63
|
+
process.stderr.write(`loophaus: writing invalid state: ${result.errors.join(", ")}\n`);
|
|
64
|
+
}
|
|
65
|
+
const statePath = getStatePath(cwd, name);
|
|
66
|
+
await mkdir(dirname(statePath), { recursive: true });
|
|
67
|
+
const tmp = statePath + ".tmp";
|
|
68
|
+
await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
69
|
+
await rename(tmp, statePath);
|
|
70
|
+
}
|
|
71
|
+
export async function reset(cwd) {
|
|
72
|
+
await write({ ...DEFAULT_STATE }, cwd);
|
|
73
|
+
}
|
|
74
|
+
function migrateMdFormat(raw) {
|
|
75
|
+
const state = { ...DEFAULT_STATE };
|
|
76
|
+
const lines = raw.split("\n");
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
79
|
+
if (!match)
|
|
80
|
+
continue;
|
|
81
|
+
const [, key, value] = match;
|
|
82
|
+
if (key === "active")
|
|
83
|
+
state.active = value.trim() === "true";
|
|
84
|
+
else if (key === "iteration")
|
|
85
|
+
state.currentIteration = parseInt(value.trim(), 10) || 0;
|
|
86
|
+
else if (key === "max_iterations")
|
|
87
|
+
state.maxIterations = parseInt(value.trim(), 10) || 20;
|
|
88
|
+
else if (key === "completion_promise")
|
|
89
|
+
state.completionPromise = value.trim();
|
|
90
|
+
else if (key === "prompt")
|
|
91
|
+
state.prompt = value.trim();
|
|
92
|
+
else if (key === "session_id")
|
|
93
|
+
state.sessionId = value.trim();
|
|
94
|
+
}
|
|
95
|
+
return state;
|
|
96
|
+
}
|
|
97
|
+
// Backward-compatible exports (matching lib/state.mjs interface)
|
|
98
|
+
export async function readState(cwd) { return read(cwd); }
|
|
99
|
+
export async function writeState(state, cwd) { return write(state, cwd); }
|
|
100
|
+
export async function resetState(cwd) { return reset(cwd); }
|
|
101
|
+
export async function incrementIteration(cwd) {
|
|
102
|
+
const state = await read(cwd);
|
|
103
|
+
state.currentIteration += 1;
|
|
104
|
+
await write(state, cwd);
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
export { DEFAULT_STATE };
|
|
108
|
+
//# sourceMappingURL=state-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.js","sourceRoot":"","sources":["../../store/state-store.ts"],"names":[],"mappings":"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"}
|
package/hooks/stop-hook.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { evaluateStopHook } from "../core/engine.
|
|
4
|
-
import { getLastAssistantText, hasPendingStories } from "../core/io-helpers.
|
|
5
|
-
import { read as readState, write as writeState } from "../store/state-store.
|
|
6
|
-
import { logEvents } from "../core/event-logger.
|
|
3
|
+
import { evaluateStopHook } from "../core/engine.js";
|
|
4
|
+
import { getLastAssistantText, hasPendingStories } from "../core/io-helpers.js";
|
|
5
|
+
import { read as readState, write as writeState } from "../store/state-store.js";
|
|
6
|
+
import { logEvents } from "../core/event-logger.js";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
9
|
async function runStoryTests(cwd) {
|
|
@@ -69,7 +69,7 @@ async function main() {
|
|
|
69
69
|
const testResults = await runStoryTests(cwd);
|
|
70
70
|
|
|
71
71
|
// Evaluate loop policy
|
|
72
|
-
const { loadPolicy, evaluatePolicy } = await import("../core/policy.
|
|
72
|
+
const { loadPolicy, evaluatePolicy } = await import("../core/policy.js");
|
|
73
73
|
const policy = await loadPolicy(cwd);
|
|
74
74
|
const policyResult = evaluatePolicy(policy, state, { totalCost: 0, errorCount: 0 });
|
|
75
75
|
|
|
@@ -89,7 +89,7 @@ async function main() {
|
|
|
89
89
|
|
|
90
90
|
// Save session checkpoint (best-effort)
|
|
91
91
|
try {
|
|
92
|
-
const { saveCheckpoint } = await import("../core/session.
|
|
92
|
+
const { saveCheckpoint } = await import("../core/session.js");
|
|
93
93
|
await saveCheckpoint(result.nextState.sessionId || `auto-${Date.now()}`, {
|
|
94
94
|
prompt: result.nextState.prompt,
|
|
95
95
|
completionPromise: result.nextState.completionPromise,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graypark/loophaus",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "loophaus — Control plane for coding agents. Iterative dev loops with multi-agent orchestration.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,18 +17,15 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"bin": {
|
|
20
|
-
"loophaus": "./bin/loophaus.
|
|
20
|
+
"loophaus": "./dist/bin/loophaus.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"
|
|
23
|
+
"dist/",
|
|
24
24
|
"hooks/",
|
|
25
25
|
"commands/",
|
|
26
26
|
"codex/",
|
|
27
|
-
"core/",
|
|
28
|
-
"store/",
|
|
29
27
|
"platforms/",
|
|
30
28
|
"scripts/",
|
|
31
|
-
"lib/",
|
|
32
29
|
"skills/",
|
|
33
30
|
".claude-plugin/",
|
|
34
31
|
"README.md",
|
|
@@ -36,8 +33,13 @@
|
|
|
36
33
|
"LICENSE"
|
|
37
34
|
],
|
|
38
35
|
"scripts": {
|
|
36
|
+
"build": "tsc && npm run postbuild",
|
|
37
|
+
"postbuild": "node -e \"const fs=require('fs');const p=require('path');['platforms','hooks','codex','commands','scripts','skills','.claude-plugin'].forEach(d=>{const s=p.join(__dirname,d);const t=p.join(__dirname,'dist',d);if(fs.existsSync(s))fs.cpSync(s,t,{recursive:true})});['package.json','LICENSE','README.md','README.ko.md'].forEach(f=>{const s=p.join(__dirname,f);const t=p.join(__dirname,'dist',f);if(fs.existsSync(s))fs.cpSync(s,t)})\"",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
39
|
"test": "vitest run",
|
|
40
|
-
"postinstall": "echo 'Run: npx loophaus --help'"
|
|
40
|
+
"postinstall": "echo 'Run: npx loophaus --help'",
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
42
|
+
"prepare": "npm run build"
|
|
41
43
|
},
|
|
42
44
|
"engines": {
|
|
43
45
|
"node": ">=20.0.0"
|
|
@@ -52,6 +54,8 @@
|
|
|
52
54
|
"coding-agents"
|
|
53
55
|
],
|
|
54
56
|
"devDependencies": {
|
|
57
|
+
"@types/node": "^25.5.0",
|
|
58
|
+
"typescript": "^6.0.2",
|
|
55
59
|
"vitest": "^4.1.0"
|
|
56
60
|
}
|
|
57
61
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getClaudeSettingsPath,
|
|
10
10
|
getClaudeInstalledPluginsPath,
|
|
11
11
|
getPackageVersion,
|
|
12
|
-
} from "../../lib/paths.
|
|
12
|
+
} from "../../lib/paths.js";
|
|
13
13
|
|
|
14
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
15
|
const PROJECT_ROOT = resolve(dirname(__filename), "../..");
|
|
@@ -168,6 +168,6 @@ export async function install({ dryRun = false, force = false } = {}) {
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
export async function uninstall({ dryRun = false } = {}) {
|
|
171
|
-
const { uninstall: doUninstall } = await import("../../bin/uninstall.
|
|
171
|
+
const { uninstall: doUninstall } = await import("../../bin/uninstall.js");
|
|
172
172
|
return doUninstall({ dryRun, claude: true });
|
|
173
173
|
}
|