@graypark/loophaus 3.6.0 → 3.7.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/commands/loop-plan.md +47 -0
- package/commands/loop.md +11 -0
- package/dist/bin/install.d.ts +0 -1
- package/dist/bin/install.js +0 -1
- package/dist/bin/loophaus.d.ts +0 -1
- package/dist/bin/loophaus.js +205 -3
- package/dist/bin/uninstall.d.ts +0 -1
- package/dist/bin/uninstall.js +0 -1
- package/dist/commands/loop-plan.md +47 -0
- package/dist/commands/loop.md +11 -0
- package/dist/core/benchmark.d.ts +0 -1
- package/dist/core/benchmark.js +0 -1
- package/dist/core/cleanup.d.ts +0 -1
- package/dist/core/cleanup.js +0 -1
- package/dist/core/cost-tracker.d.ts +0 -1
- package/dist/core/cost-tracker.js +0 -1
- package/dist/core/engine.d.ts +0 -1
- package/dist/core/engine.js +0 -1
- package/dist/core/event-logger.d.ts +0 -1
- package/dist/core/event-logger.js +0 -1
- package/dist/core/events.d.ts +0 -1
- package/dist/core/events.js +0 -1
- package/dist/core/io-helpers.d.ts +0 -1
- package/dist/core/io-helpers.js +0 -1
- package/dist/core/loop-registry.d.ts +0 -1
- package/dist/core/loop-registry.js +0 -1
- package/dist/core/merge-strategy.d.ts +0 -1
- package/dist/core/merge-strategy.js +0 -1
- package/dist/core/parallel-runner.d.ts +0 -1
- package/dist/core/parallel-runner.js +0 -1
- package/dist/core/policy.d.ts +0 -1
- package/dist/core/policy.js +0 -1
- package/dist/core/quality-scorer.d.ts +0 -1
- package/dist/core/quality-scorer.js +0 -1
- package/dist/core/refine-loop.d.ts +0 -1
- package/dist/core/refine-loop.js +0 -1
- package/dist/core/session.d.ts +0 -1
- package/dist/core/session.js +0 -1
- package/dist/core/trace-analyzer.d.ts +0 -1
- package/dist/core/trace-analyzer.js +0 -1
- package/dist/core/types.d.ts +0 -1
- package/dist/core/types.js +0 -1
- package/dist/core/update-checker.d.ts +30 -0
- package/dist/core/update-checker.js +172 -0
- package/dist/core/validate.d.ts +0 -1
- package/dist/core/validate.js +0 -1
- package/dist/core/worktree.d.ts +0 -1
- package/dist/core/worktree.js +0 -1
- package/dist/lib/paths.d.ts +0 -1
- package/dist/lib/paths.js +0 -1
- package/dist/lib/stop-hook-core.d.ts +0 -1
- package/dist/lib/stop-hook-core.js +0 -1
- package/dist/package.json +1 -1
- package/dist/store/state-store.d.ts +0 -1
- package/dist/store/state-store.js +0 -1
- package/package.json +1 -1
- package/dist/bin/install.d.ts.map +0 -1
- package/dist/bin/install.js.map +0 -1
- package/dist/bin/loophaus.d.ts.map +0 -1
- package/dist/bin/loophaus.js.map +0 -1
- package/dist/bin/uninstall.d.ts.map +0 -1
- package/dist/bin/uninstall.js.map +0 -1
- package/dist/core/benchmark.d.ts.map +0 -1
- package/dist/core/benchmark.js.map +0 -1
- package/dist/core/cleanup.d.ts.map +0 -1
- package/dist/core/cleanup.js.map +0 -1
- package/dist/core/cost-tracker.d.ts.map +0 -1
- package/dist/core/cost-tracker.js.map +0 -1
- package/dist/core/engine.d.ts.map +0 -1
- package/dist/core/engine.js.map +0 -1
- package/dist/core/event-logger.d.ts.map +0 -1
- package/dist/core/event-logger.js.map +0 -1
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js.map +0 -1
- package/dist/core/io-helpers.d.ts.map +0 -1
- package/dist/core/io-helpers.js.map +0 -1
- package/dist/core/loop-registry.d.ts.map +0 -1
- package/dist/core/loop-registry.js.map +0 -1
- package/dist/core/merge-strategy.d.ts.map +0 -1
- package/dist/core/merge-strategy.js.map +0 -1
- package/dist/core/parallel-runner.d.ts.map +0 -1
- package/dist/core/parallel-runner.js.map +0 -1
- package/dist/core/policy.d.ts.map +0 -1
- package/dist/core/policy.js.map +0 -1
- package/dist/core/quality-scorer.d.ts.map +0 -1
- package/dist/core/quality-scorer.js.map +0 -1
- package/dist/core/refine-loop.d.ts.map +0 -1
- package/dist/core/refine-loop.js.map +0 -1
- package/dist/core/session.d.ts.map +0 -1
- package/dist/core/session.js.map +0 -1
- package/dist/core/trace-analyzer.d.ts.map +0 -1
- package/dist/core/trace-analyzer.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/core/validate.d.ts.map +0 -1
- package/dist/core/validate.js.map +0 -1
- package/dist/core/worktree.d.ts.map +0 -1
- package/dist/core/worktree.js.map +0 -1
- package/dist/lib/paths.d.ts.map +0 -1
- package/dist/lib/paths.js.map +0 -1
- package/dist/lib/stop-hook-core.d.ts.map +0 -1
- package/dist/lib/stop-hook-core.js.map +0 -1
- package/dist/store/state-store.d.ts.map +0 -1
- package/dist/store/state-store.js.map +0 -1
package/commands/loop-plan.md
CHANGED
|
@@ -11,6 +11,53 @@ The user runs `/loop-plan` once and gets a single merged branch with all work do
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## Pre-flight: Update Check
|
|
15
|
+
|
|
16
|
+
Run this silently before starting:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
_UPDATE=$(loophaus update-check 2>/dev/null | grep "UPGRADE_AVAILABLE" || true)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If `_UPDATE` contains `UPGRADE_AVAILABLE`:
|
|
23
|
+
1. Show: `loophaus update available: v{current} → v{latest}`
|
|
24
|
+
2. Ask user:
|
|
25
|
+
- **A) Upgrade now** → run `loophaus upgrade`, then continue
|
|
26
|
+
- **B) Always auto-upgrade** → run `loophaus config set autoUpgrade true && loophaus upgrade`, then continue
|
|
27
|
+
- **C) Not now** → run `loophaus config set updateCheck snoozed` silently, then continue
|
|
28
|
+
- **D) Never ask again** → run `loophaus config set updateCheck false`, then continue
|
|
29
|
+
|
|
30
|
+
If auto-upgrade is enabled (`loophaus config get autoUpgrade` returns `true`):
|
|
31
|
+
- Skip prompt, run `loophaus upgrade` silently, continue.
|
|
32
|
+
|
|
33
|
+
If no update or check fails: continue silently (never block the user).
|
|
34
|
+
|
|
35
|
+
## Pre-flight: Skill Routing Check
|
|
36
|
+
|
|
37
|
+
Check if the project has a CLAUDE.md with loophaus skill routing:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
CLAUDE_MD="$(pwd)/CLAUDE.md"
|
|
41
|
+
ROUTING_MARKER=".loophaus/routing-offered.json"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If CLAUDE.md exists but has no `## loophaus skill routing` section, AND routing hasn't been offered before:
|
|
45
|
+
1. Suggest adding this section to CLAUDE.md:
|
|
46
|
+
```markdown
|
|
47
|
+
## loophaus skill routing
|
|
48
|
+
|
|
49
|
+
| User intent | Route to |
|
|
50
|
+
|-------------|----------|
|
|
51
|
+
| Feature implementation, refactoring, multi-step task | `/loophaus:loop-plan` |
|
|
52
|
+
| "start the loop", "continue implementing" | `/loophaus:loop` |
|
|
53
|
+
| "stop the loop", "cancel" | `/loophaus:loop-stop` |
|
|
54
|
+
| "what's the status", "how far along" | `/loophaus:loop-pulse` |
|
|
55
|
+
```
|
|
56
|
+
2. If user agrees, add the section to CLAUDE.md
|
|
57
|
+
3. Write `{ "offeredAt": "<ISO date>" }` to `.loophaus/routing-offered.json` so we don't ask again
|
|
58
|
+
|
|
59
|
+
If CLAUDE.md doesn't exist or routing already offered: skip silently.
|
|
60
|
+
|
|
14
61
|
## Phase 0: Cleanup Previous Data
|
|
15
62
|
|
|
16
63
|
Before starting a new plan, apply the cleanup policy from `.loophaus/config.json`:
|
package/commands/loop.md
CHANGED
|
@@ -6,6 +6,17 @@ allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"]
|
|
|
6
6
|
|
|
7
7
|
# /loop — Start Iterative Dev Loop
|
|
8
8
|
|
|
9
|
+
## Pre-flight
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
_UPDATE=$(loophaus update-check 2>/dev/null | grep "UPGRADE_AVAILABLE" || true)
|
|
13
|
+
```
|
|
14
|
+
If update available and autoUpgrade enabled: run `loophaus upgrade` silently.
|
|
15
|
+
If update available and not auto: show one-line notice `loophaus v{new} available. Run: loophaus upgrade`.
|
|
16
|
+
Otherwise: continue silently.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
9
20
|
Execute the setup script to initialize the loop:
|
|
10
21
|
|
|
11
22
|
```!
|
package/dist/bin/install.d.ts
CHANGED
package/dist/bin/install.js
CHANGED
package/dist/bin/loophaus.d.ts
CHANGED
package/dist/bin/loophaus.js
CHANGED
|
@@ -16,12 +16,13 @@ const showHelp = args.includes("--help") || args.includes("-h");
|
|
|
16
16
|
const KNOWN_FLAGS = new Set([
|
|
17
17
|
"--help", "-h", "--version", "--dry-run", "--force", "--local", "--verbose",
|
|
18
18
|
"--host", "--claude", "--kiro", "--name", "--speed", "--count", "--base", "--story",
|
|
19
|
-
"--all", "--traces", "--sessions", "--results", "--before", "--config",
|
|
19
|
+
"--all", "--traces", "--sessions", "--results", "--before", "--config", "--quiet",
|
|
20
20
|
]);
|
|
21
21
|
const VALID_COMMANDS = [
|
|
22
22
|
"install", "uninstall", "status", "stats", "loops", "watch",
|
|
23
23
|
"replay", "compare", "worktree", "parallel", "quality",
|
|
24
|
-
"sessions", "resume", "benchmark", "clean", "
|
|
24
|
+
"sessions", "resume", "benchmark", "clean", "config",
|
|
25
|
+
"update-check", "upgrade", "help",
|
|
25
26
|
];
|
|
26
27
|
function validateFlags() {
|
|
27
28
|
for (const arg of args) {
|
|
@@ -114,6 +115,9 @@ Usage:
|
|
|
114
115
|
npx @graypark/loophaus quality [--story US-001]
|
|
115
116
|
npx @graypark/loophaus benchmark
|
|
116
117
|
npx @graypark/loophaus clean [--all|--traces|--sessions|--results] [--before DATE]
|
|
118
|
+
npx @graypark/loophaus config [list|get|set] [key] [value]
|
|
119
|
+
npx @graypark/loophaus update-check
|
|
120
|
+
npx @graypark/loophaus upgrade
|
|
117
121
|
npx @graypark/loophaus sessions
|
|
118
122
|
npx @graypark/loophaus resume <session-id>
|
|
119
123
|
npx @graypark/loophaus --version
|
|
@@ -156,6 +160,11 @@ async function detectHosts() {
|
|
|
156
160
|
return hosts;
|
|
157
161
|
}
|
|
158
162
|
async function runInstall() {
|
|
163
|
+
const { getPackageVersion } = await import("../lib/paths.js");
|
|
164
|
+
const version = getPackageVersion();
|
|
165
|
+
const quiet = args.includes("--quiet");
|
|
166
|
+
const loophausDir = join(process.env.HOME || "~", ".loophaus");
|
|
167
|
+
const welcomePath = join(loophausDir, ".welcome-seen");
|
|
159
168
|
let targets = [];
|
|
160
169
|
if (host) {
|
|
161
170
|
targets = [host];
|
|
@@ -192,6 +201,57 @@ async function runInstall() {
|
|
|
192
201
|
s?.stop();
|
|
193
202
|
}
|
|
194
203
|
}
|
|
204
|
+
if (quiet || dryRun)
|
|
205
|
+
return;
|
|
206
|
+
// First-run welcome or upgrade notice
|
|
207
|
+
const { mkdir: mk, writeFile: wf, readFile: rf } = await import("node:fs/promises");
|
|
208
|
+
await mk(loophausDir, { recursive: true });
|
|
209
|
+
let isFirstRun = false;
|
|
210
|
+
try {
|
|
211
|
+
const seen = await rf(welcomePath, "utf-8");
|
|
212
|
+
// Existing install — show What's New if version changed
|
|
213
|
+
if (seen.trim() !== version) {
|
|
214
|
+
await wf(welcomePath, version, "utf-8");
|
|
215
|
+
try {
|
|
216
|
+
const changelog = await rf(join(__dirname, "..", "CHANGELOG.md"), "utf-8");
|
|
217
|
+
const firstEntry = changelog.match(/## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/);
|
|
218
|
+
if (firstEntry) {
|
|
219
|
+
console.log(`\n \x1b[36mWhat's New in v${version}:\x1b[0m`);
|
|
220
|
+
const lines = firstEntry[1].trim().split("\n").slice(0, 8);
|
|
221
|
+
for (const l of lines)
|
|
222
|
+
console.log(` ${l}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch { /* no CHANGELOG */ }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
isFirstRun = true;
|
|
230
|
+
await wf(welcomePath, version, "utf-8");
|
|
231
|
+
}
|
|
232
|
+
if (isFirstRun) {
|
|
233
|
+
console.log(`
|
|
234
|
+
\x1b[36m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1b[0m
|
|
235
|
+
Welcome to \x1b[1mloophaus\x1b[0m v${version}
|
|
236
|
+
|
|
237
|
+
Control plane for coding agents.
|
|
238
|
+
Iterative dev loops with quality verification.
|
|
239
|
+
|
|
240
|
+
Quick start:
|
|
241
|
+
/loop-plan <describe your task>
|
|
242
|
+
|
|
243
|
+
Commands:
|
|
244
|
+
/loop-plan Interview → PRD → implement → verify
|
|
245
|
+
/loop Start loop with existing PRD
|
|
246
|
+
/loop-pulse Check progress
|
|
247
|
+
/loop-stop Cancel loop
|
|
248
|
+
|
|
249
|
+
CLI:
|
|
250
|
+
loophaus benchmark Project quality score
|
|
251
|
+
loophaus config list View settings
|
|
252
|
+
loophaus upgrade Update to latest
|
|
253
|
+
\x1b[36m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1b[0m`);
|
|
254
|
+
}
|
|
195
255
|
}
|
|
196
256
|
async function runUninstall() {
|
|
197
257
|
if (host === "claude-code" || args.includes("--claude")) {
|
|
@@ -683,6 +743,140 @@ Options:
|
|
|
683
743
|
console.log(" Nothing to clean.");
|
|
684
744
|
}
|
|
685
745
|
}
|
|
746
|
+
async function runUpdateCheck() {
|
|
747
|
+
const { getPackageVersion } = await import("../lib/paths.js");
|
|
748
|
+
const { checkForUpdate } = await import("../core/update-checker.js");
|
|
749
|
+
const current = getPackageVersion();
|
|
750
|
+
const result = await checkForUpdate(current);
|
|
751
|
+
console.log("Update Check");
|
|
752
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
753
|
+
console.log(` Current: v${result.current}`);
|
|
754
|
+
console.log(` Latest: v${result.latest}`);
|
|
755
|
+
console.log(` Status: ${result.status}`);
|
|
756
|
+
if (result.message)
|
|
757
|
+
console.log(` Note: ${result.message}`);
|
|
758
|
+
if (result.status === "upgrade_available") {
|
|
759
|
+
console.log(`\n \x1b[33mUpdate available: v${result.current} → v${result.latest}\x1b[0m`);
|
|
760
|
+
console.log(` Run: loophaus upgrade`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async function runUpgrade() {
|
|
764
|
+
const { getPackageVersion } = await import("../lib/paths.js");
|
|
765
|
+
const { checkForUpdate } = await import("../core/update-checker.js");
|
|
766
|
+
const { execFile: ef } = await import("node:child_process");
|
|
767
|
+
const { promisify } = await import("node:util");
|
|
768
|
+
const execFileAsync = promisify(ef);
|
|
769
|
+
const current = getPackageVersion();
|
|
770
|
+
const result = await checkForUpdate(current);
|
|
771
|
+
if (result.status === "up_to_date") {
|
|
772
|
+
console.log(`Already on latest version: v${current}`);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (result.status !== "upgrade_available" && result.status !== "snoozed") {
|
|
776
|
+
console.log(`No update available (status: ${result.status})`);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
console.log(`Upgrading loophaus: v${result.current} → v${result.latest}`);
|
|
780
|
+
const s = spinner("Installing...");
|
|
781
|
+
try {
|
|
782
|
+
await execFileAsync("npm", ["install", "-g", `@graypark/loophaus@${result.latest}`], { timeout: 120_000 });
|
|
783
|
+
s.stop();
|
|
784
|
+
console.log(`\u2714 Installed v${result.latest}`);
|
|
785
|
+
const s2 = spinner("Reinstalling plugins...");
|
|
786
|
+
try {
|
|
787
|
+
await execFileAsync("loophaus", ["install", "--force"], { timeout: 60_000 });
|
|
788
|
+
s2.stop();
|
|
789
|
+
console.log("\u2714 Plugins reinstalled");
|
|
790
|
+
}
|
|
791
|
+
catch {
|
|
792
|
+
s2.stop();
|
|
793
|
+
console.log(" Note: Run 'loophaus install --force' to update plugins.");
|
|
794
|
+
}
|
|
795
|
+
console.log(`\n Upgrade complete: v${result.current} → v${result.latest}`);
|
|
796
|
+
}
|
|
797
|
+
catch (err) {
|
|
798
|
+
s.stop();
|
|
799
|
+
console.error(`\u2718 Upgrade failed: ${err.message}`);
|
|
800
|
+
console.error(" Try manually: npm install -g @graypark/loophaus@latest");
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
async function runConfigCmd() {
|
|
804
|
+
const { readConfig, writeConfig } = await import("../core/cleanup.js");
|
|
805
|
+
const sub = args[1];
|
|
806
|
+
const KNOWN_KEYS = {
|
|
807
|
+
"cleanup.onNewPlan": "Policy when /loop-plan starts: archive | delete | keep",
|
|
808
|
+
"cleanup.traceRetentionDays": "Days to keep trace data",
|
|
809
|
+
"cleanup.sessionRetentionDays": "Days to keep session checkpoints",
|
|
810
|
+
"updateCheck": "Check for updates on skill execution: true | false",
|
|
811
|
+
"autoUpgrade": "Auto-upgrade without prompting: true | false",
|
|
812
|
+
};
|
|
813
|
+
if (!sub || sub === "list") {
|
|
814
|
+
const config = await readConfig();
|
|
815
|
+
console.log("Configuration (.loophaus/config.json)");
|
|
816
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
|
|
817
|
+
for (const [key, desc] of Object.entries(KNOWN_KEYS)) {
|
|
818
|
+
const val = getNestedValue(config, key);
|
|
819
|
+
console.log(` ${key.padEnd(30)} ${String(val ?? "(default)").padEnd(12)} ${desc}`);
|
|
820
|
+
}
|
|
821
|
+
console.log(`\nUsage: loophaus config set <key> <value>`);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (sub === "get") {
|
|
825
|
+
const key = args[2];
|
|
826
|
+
if (!key) {
|
|
827
|
+
console.log("Usage: loophaus config get <key>");
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const config = await readConfig();
|
|
831
|
+
const val = getNestedValue(config, key);
|
|
832
|
+
console.log(val !== undefined ? String(val) : "(not set)");
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (sub === "set") {
|
|
836
|
+
const key = args[3] ? args[2] : args[2];
|
|
837
|
+
const value = args[3] || args[3];
|
|
838
|
+
if (!key || value === undefined) {
|
|
839
|
+
console.log("Usage: loophaus config set <key> <value>");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const rawValue = args[3];
|
|
843
|
+
if (!key || rawValue === undefined) {
|
|
844
|
+
console.log("Usage: loophaus config set <key> <value>");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (!KNOWN_KEYS[key]) {
|
|
848
|
+
console.log(`Warning: '${key}' is not a known config key.`);
|
|
849
|
+
}
|
|
850
|
+
const config = await readConfig();
|
|
851
|
+
const parsed = rawValue === "true" ? true : rawValue === "false" ? false : isNaN(Number(rawValue)) ? rawValue : Number(rawValue);
|
|
852
|
+
setNestedValue(config, key, parsed);
|
|
853
|
+
await writeConfig(config);
|
|
854
|
+
console.log(`Set ${key} = ${String(parsed)}`);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
console.log("Usage: loophaus config [list|get|set] [key] [value]");
|
|
858
|
+
}
|
|
859
|
+
function getNestedValue(obj, path) {
|
|
860
|
+
const parts = path.split(".");
|
|
861
|
+
let current = obj;
|
|
862
|
+
for (const part of parts) {
|
|
863
|
+
if (current == null || typeof current !== "object")
|
|
864
|
+
return undefined;
|
|
865
|
+
current = current[part];
|
|
866
|
+
}
|
|
867
|
+
return current;
|
|
868
|
+
}
|
|
869
|
+
function setNestedValue(obj, path, value) {
|
|
870
|
+
const parts = path.split(".");
|
|
871
|
+
let current = obj;
|
|
872
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
873
|
+
if (!(parts[i] in current) || typeof current[parts[i]] !== "object") {
|
|
874
|
+
current[parts[i]] = {};
|
|
875
|
+
}
|
|
876
|
+
current = current[parts[i]];
|
|
877
|
+
}
|
|
878
|
+
current[parts[parts.length - 1]] = value;
|
|
879
|
+
}
|
|
686
880
|
try {
|
|
687
881
|
switch (command) {
|
|
688
882
|
case "install":
|
|
@@ -724,6 +918,15 @@ try {
|
|
|
724
918
|
case "clean":
|
|
725
919
|
await runCleanCmd();
|
|
726
920
|
break;
|
|
921
|
+
case "config":
|
|
922
|
+
await runConfigCmd();
|
|
923
|
+
break;
|
|
924
|
+
case "update-check":
|
|
925
|
+
await runUpdateCheck();
|
|
926
|
+
break;
|
|
927
|
+
case "upgrade":
|
|
928
|
+
await runUpgrade();
|
|
929
|
+
break;
|
|
727
930
|
case "sessions":
|
|
728
931
|
await runSessions();
|
|
729
932
|
break;
|
|
@@ -761,4 +964,3 @@ catch (err) {
|
|
|
761
964
|
}
|
|
762
965
|
process.exit(1);
|
|
763
966
|
}
|
|
764
|
-
//# sourceMappingURL=loophaus.js.map
|
package/dist/bin/uninstall.d.ts
CHANGED
package/dist/bin/uninstall.js
CHANGED
|
@@ -11,6 +11,53 @@ The user runs `/loop-plan` once and gets a single merged branch with all work do
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## Pre-flight: Update Check
|
|
15
|
+
|
|
16
|
+
Run this silently before starting:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
_UPDATE=$(loophaus update-check 2>/dev/null | grep "UPGRADE_AVAILABLE" || true)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If `_UPDATE` contains `UPGRADE_AVAILABLE`:
|
|
23
|
+
1. Show: `loophaus update available: v{current} → v{latest}`
|
|
24
|
+
2. Ask user:
|
|
25
|
+
- **A) Upgrade now** → run `loophaus upgrade`, then continue
|
|
26
|
+
- **B) Always auto-upgrade** → run `loophaus config set autoUpgrade true && loophaus upgrade`, then continue
|
|
27
|
+
- **C) Not now** → run `loophaus config set updateCheck snoozed` silently, then continue
|
|
28
|
+
- **D) Never ask again** → run `loophaus config set updateCheck false`, then continue
|
|
29
|
+
|
|
30
|
+
If auto-upgrade is enabled (`loophaus config get autoUpgrade` returns `true`):
|
|
31
|
+
- Skip prompt, run `loophaus upgrade` silently, continue.
|
|
32
|
+
|
|
33
|
+
If no update or check fails: continue silently (never block the user).
|
|
34
|
+
|
|
35
|
+
## Pre-flight: Skill Routing Check
|
|
36
|
+
|
|
37
|
+
Check if the project has a CLAUDE.md with loophaus skill routing:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
CLAUDE_MD="$(pwd)/CLAUDE.md"
|
|
41
|
+
ROUTING_MARKER=".loophaus/routing-offered.json"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If CLAUDE.md exists but has no `## loophaus skill routing` section, AND routing hasn't been offered before:
|
|
45
|
+
1. Suggest adding this section to CLAUDE.md:
|
|
46
|
+
```markdown
|
|
47
|
+
## loophaus skill routing
|
|
48
|
+
|
|
49
|
+
| User intent | Route to |
|
|
50
|
+
|-------------|----------|
|
|
51
|
+
| Feature implementation, refactoring, multi-step task | `/loophaus:loop-plan` |
|
|
52
|
+
| "start the loop", "continue implementing" | `/loophaus:loop` |
|
|
53
|
+
| "stop the loop", "cancel" | `/loophaus:loop-stop` |
|
|
54
|
+
| "what's the status", "how far along" | `/loophaus:loop-pulse` |
|
|
55
|
+
```
|
|
56
|
+
2. If user agrees, add the section to CLAUDE.md
|
|
57
|
+
3. Write `{ "offeredAt": "<ISO date>" }` to `.loophaus/routing-offered.json` so we don't ask again
|
|
58
|
+
|
|
59
|
+
If CLAUDE.md doesn't exist or routing already offered: skip silently.
|
|
60
|
+
|
|
14
61
|
## Phase 0: Cleanup Previous Data
|
|
15
62
|
|
|
16
63
|
Before starting a new plan, apply the cleanup policy from `.loophaus/config.json`:
|
package/dist/commands/loop.md
CHANGED
|
@@ -6,6 +6,17 @@ allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"]
|
|
|
6
6
|
|
|
7
7
|
# /loop — Start Iterative Dev Loop
|
|
8
8
|
|
|
9
|
+
## Pre-flight
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
_UPDATE=$(loophaus update-check 2>/dev/null | grep "UPGRADE_AVAILABLE" || true)
|
|
13
|
+
```
|
|
14
|
+
If update available and autoUpgrade enabled: run `loophaus upgrade` silently.
|
|
15
|
+
If update available and not auto: show one-line notice `loophaus v{new} available. Run: loophaus upgrade`.
|
|
16
|
+
Otherwise: continue silently.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
9
20
|
Execute the setup script to initialize the loop:
|
|
10
21
|
|
|
11
22
|
```!
|
package/dist/core/benchmark.d.ts
CHANGED
|
@@ -36,4 +36,3 @@ export declare function scoreBenchmark(metrics: BenchmarkMetrics): BenchmarkResu
|
|
|
36
36
|
export declare function runBenchmark(cwd?: string): Promise<BenchmarkResult>;
|
|
37
37
|
export declare function logBenchmark(result: BenchmarkResult, cwd?: string): Promise<void>;
|
|
38
38
|
export declare function readBenchmarkHistory(cwd?: string): Promise<BenchmarkEntry[]>;
|
|
39
|
-
//# sourceMappingURL=benchmark.d.ts.map
|
package/dist/core/benchmark.js
CHANGED
package/dist/core/cleanup.d.ts
CHANGED
|
@@ -21,4 +21,3 @@ export declare function cleanSessions(options?: {
|
|
|
21
21
|
export declare function cleanAll(cwd?: string): Promise<CleanResult>;
|
|
22
22
|
export declare function archiveCurrentData(cwd?: string): Promise<CleanResult>;
|
|
23
23
|
export declare function applyOnNewPlanPolicy(cwd?: string): Promise<CleanResult>;
|
|
24
|
-
//# sourceMappingURL=cleanup.d.ts.map
|
package/dist/core/cleanup.js
CHANGED
|
@@ -30,4 +30,3 @@ export declare function estimateCost(model: string, inputTokens: number, outputT
|
|
|
30
30
|
export declare function formatCost(cost: number): string;
|
|
31
31
|
export declare function createTracker(): CostTracker;
|
|
32
32
|
export { MODEL_PRICES };
|
|
33
|
-
//# sourceMappingURL=cost-tracker.d.ts.map
|
package/dist/core/engine.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { LoopState, StopHookInput, StopHookResult } from "./types.js";
|
|
2
2
|
export declare function evaluateStopHook(input: StopHookInput, state: LoopState): StopHookResult;
|
|
3
3
|
export declare function extractPromise(text: string, promisePhrase: string): boolean;
|
|
4
|
-
//# sourceMappingURL=engine.d.ts.map
|
package/dist/core/engine.js
CHANGED
|
@@ -2,4 +2,3 @@ import type { LoopEvent } from "./types.js";
|
|
|
2
2
|
export declare function getTracePath(cwd?: string): string;
|
|
3
3
|
export declare function logEvents(events: LoopEvent[], metadata?: Record<string, unknown>, cwd?: string): Promise<void>;
|
|
4
4
|
export declare function readTrace(cwd?: string): Promise<Record<string, unknown>[]>;
|
|
5
|
-
//# sourceMappingURL=event-logger.d.ts.map
|
package/dist/core/events.d.ts
CHANGED
package/dist/core/events.js
CHANGED
|
@@ -41,4 +41,3 @@ export function summarizeEvents(events) {
|
|
|
41
41
|
const durationMs = first && last ? new Date(last.ts).getTime() - new Date(first.ts).getTime() : 0;
|
|
42
42
|
return { counts, total: events.length, durationMs, firstTs: first?.ts, lastTs: last?.ts };
|
|
43
43
|
}
|
|
44
|
-
//# sourceMappingURL=events.js.map
|
package/dist/core/io-helpers.js
CHANGED
|
@@ -4,4 +4,3 @@ export declare function mergeSequential(branches: string[], targetBranch?: strin
|
|
|
4
4
|
export declare function mergeSquash(branches: string[]): Promise<MergeResult[]>;
|
|
5
5
|
export declare function mergeCherryPick(branches: string[]): Promise<MergeResult[]>;
|
|
6
6
|
export declare function merge(strategy: string, branches: string[], targetBranch?: string): Promise<MergeResult[]>;
|
|
7
|
-
//# sourceMappingURL=merge-strategy.d.ts.map
|
package/dist/core/policy.d.ts
CHANGED
|
@@ -19,4 +19,3 @@ declare const DEFAULT_POLICY: Policy;
|
|
|
19
19
|
export declare function loadPolicy(cwd?: string): Promise<Policy>;
|
|
20
20
|
export declare function evaluatePolicy(policy: Policy, state: PolicyState, context?: PolicyContext): PolicyResult;
|
|
21
21
|
export { DEFAULT_POLICY };
|
|
22
|
-
//# sourceMappingURL=policy.d.ts.map
|
package/dist/core/policy.js
CHANGED
|
@@ -13,4 +13,3 @@ export declare function shouldKeep(newScore: number, baselineScore: number): boo
|
|
|
13
13
|
export declare function generateFeedback(evaluation: Evaluation, previousAttempts?: PreviousAttempt[]): string;
|
|
14
14
|
export declare function identifyRefinementTargets(evaluations: Evaluation[], threshold?: number): Evaluation[];
|
|
15
15
|
export {};
|
|
16
|
-
//# sourceMappingURL=refine-loop.d.ts.map
|
package/dist/core/refine-loop.js
CHANGED
package/dist/core/session.d.ts
CHANGED
package/dist/core/session.js
CHANGED
|
@@ -25,4 +25,3 @@ export interface ReplayEvent extends LoopEvent {
|
|
|
25
25
|
export declare function analyzeTrace(events: LoopEvent[]): TraceAnalysis;
|
|
26
26
|
export declare function compareTraces(trace1: LoopEvent[], trace2: LoopEvent[]): TraceComparison;
|
|
27
27
|
export declare function replayTrace(events: LoopEvent[], speed?: number): ReplayEvent[];
|
|
28
|
-
//# sourceMappingURL=trace-analyzer.d.ts.map
|
package/dist/core/types.d.ts
CHANGED
package/dist/core/types.js
CHANGED