@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,108 @@
|
|
|
1
|
+
// core/worktree.ts
|
|
2
|
+
// Git worktree lifecycle management
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { mkdir, access } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
async function fileExists(p) {
|
|
9
|
+
try {
|
|
10
|
+
await access(p);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function getRepoRoot() {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"]);
|
|
20
|
+
return stdout.trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function validateWorktreeName(name) {
|
|
27
|
+
if (!name || typeof name !== "string")
|
|
28
|
+
throw new Error("Worktree name is required");
|
|
29
|
+
if (/[\/\\]/.test(name))
|
|
30
|
+
throw new Error(`Worktree name must not contain path separators: ${name}`);
|
|
31
|
+
if (name.includes(".."))
|
|
32
|
+
throw new Error(`Worktree name must not contain '..': ${name}`);
|
|
33
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name))
|
|
34
|
+
throw new Error(`Worktree name contains invalid characters: ${name}. Use alphanumeric, dot, dash, or underscore.`);
|
|
35
|
+
}
|
|
36
|
+
export async function createWorktree(name, baseBranch = "HEAD") {
|
|
37
|
+
validateWorktreeName(name);
|
|
38
|
+
const root = await getRepoRoot();
|
|
39
|
+
if (!root)
|
|
40
|
+
throw new Error("Not in a git repository");
|
|
41
|
+
const worktreePath = join(root, ".loophaus", "worktrees", name);
|
|
42
|
+
const branchName = `loophaus/${name}`;
|
|
43
|
+
if (await fileExists(worktreePath)) {
|
|
44
|
+
throw new Error(`Worktree already exists: ${name}`);
|
|
45
|
+
}
|
|
46
|
+
await mkdir(join(root, ".loophaus", "worktrees"), { recursive: true });
|
|
47
|
+
await execFileAsync("git", ["worktree", "add", "-b", branchName, worktreePath, baseBranch]);
|
|
48
|
+
return { name, path: worktreePath, branch: branchName };
|
|
49
|
+
}
|
|
50
|
+
export async function removeWorktree(name) {
|
|
51
|
+
validateWorktreeName(name);
|
|
52
|
+
const root = await getRepoRoot();
|
|
53
|
+
if (!root)
|
|
54
|
+
throw new Error("Not in a git repository");
|
|
55
|
+
const worktreePath = join(root, ".loophaus", "worktrees", name);
|
|
56
|
+
if (!(await fileExists(worktreePath))) {
|
|
57
|
+
throw new Error(`Worktree not found: ${name}`);
|
|
58
|
+
}
|
|
59
|
+
await execFileAsync("git", ["worktree", "remove", worktreePath, "--force"]);
|
|
60
|
+
const branchName = `loophaus/${name}`;
|
|
61
|
+
try {
|
|
62
|
+
await execFileAsync("git", ["branch", "-D", branchName]);
|
|
63
|
+
}
|
|
64
|
+
catch { /* branch may not exist */ }
|
|
65
|
+
return { name, removed: true };
|
|
66
|
+
}
|
|
67
|
+
export async function listWorktrees() {
|
|
68
|
+
const root = await getRepoRoot();
|
|
69
|
+
if (!root)
|
|
70
|
+
return [];
|
|
71
|
+
try {
|
|
72
|
+
const { stdout } = await execFileAsync("git", ["worktree", "list", "--porcelain"]);
|
|
73
|
+
const entries = [];
|
|
74
|
+
let current = {};
|
|
75
|
+
for (const line of stdout.split("\n")) {
|
|
76
|
+
if (line.startsWith("worktree ")) {
|
|
77
|
+
if (current.path)
|
|
78
|
+
entries.push(current);
|
|
79
|
+
current = { path: line.slice(9) };
|
|
80
|
+
}
|
|
81
|
+
else if (line.startsWith("HEAD ")) {
|
|
82
|
+
current.head = line.slice(5);
|
|
83
|
+
}
|
|
84
|
+
else if (line.startsWith("branch ")) {
|
|
85
|
+
current.branch = line.slice(7);
|
|
86
|
+
}
|
|
87
|
+
else if (line === "bare") {
|
|
88
|
+
current.bare = true;
|
|
89
|
+
}
|
|
90
|
+
else if (line === "") {
|
|
91
|
+
if (current.path)
|
|
92
|
+
entries.push(current);
|
|
93
|
+
current = {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const loophausDir = join(root, ".loophaus", "worktrees");
|
|
97
|
+
return entries.filter(e => e.path && e.path.startsWith(loophausDir)).map(e => ({
|
|
98
|
+
name: e.path.split("/").pop(),
|
|
99
|
+
path: e.path,
|
|
100
|
+
branch: e.branch || "",
|
|
101
|
+
head: e.head || "",
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../core/worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
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
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
async function runStoryTests(cwd) {
|
|
10
|
+
const { readFile } = await import("node:fs/promises");
|
|
11
|
+
const { execFile } = await import("node:child_process");
|
|
12
|
+
const { promisify } = await import("node:util");
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
const prdPath = join(cwd, "prd.json");
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const prd = JSON.parse(await readFile(prdPath, "utf-8"));
|
|
18
|
+
if (!Array.isArray(prd.userStories)) return [];
|
|
19
|
+
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const story of prd.userStories) {
|
|
22
|
+
if (!story.testCommand || story.passes) continue;
|
|
23
|
+
try {
|
|
24
|
+
await execFileAsync("sh", ["-c", story.testCommand], { cwd, timeout: 60_000 });
|
|
25
|
+
results.push({ storyId: story.id, passed: true });
|
|
26
|
+
} catch (err) {
|
|
27
|
+
results.push({ storyId: story.id, passed: false, error: err.message });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return results;
|
|
31
|
+
} catch { return []; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readStdin() {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
37
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
let hookInput = {};
|
|
42
|
+
try {
|
|
43
|
+
const raw = await readStdin();
|
|
44
|
+
if (raw.trim()) hookInput = JSON.parse(raw);
|
|
45
|
+
} catch { /* empty input */ }
|
|
46
|
+
|
|
47
|
+
const cwd = hookInput.cwd || process.cwd();
|
|
48
|
+
const state = await readState(cwd);
|
|
49
|
+
|
|
50
|
+
const lastText = hookInput.last_assistant_message ||
|
|
51
|
+
await getLastAssistantText(hookInput.transcript_path || null);
|
|
52
|
+
const pending = await hasPendingStories(cwd);
|
|
53
|
+
|
|
54
|
+
// Run verify script if configured
|
|
55
|
+
let verifyResult = null;
|
|
56
|
+
if (state.verifyScript) {
|
|
57
|
+
try {
|
|
58
|
+
const { execFile } = await import("node:child_process");
|
|
59
|
+
const { promisify } = await import("node:util");
|
|
60
|
+
const execFileAsync = promisify(execFile);
|
|
61
|
+
const { stdout: vOut } = await execFileAsync(state.verifyScript, [], { cwd, timeout: 30_000 });
|
|
62
|
+
verifyResult = { passed: true, output: vOut.trim() };
|
|
63
|
+
} catch (err) {
|
|
64
|
+
verifyResult = { passed: false, output: err.stderr || err.message };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Run story tests if prd.json has testCommand fields
|
|
69
|
+
const testResults = await runStoryTests(cwd);
|
|
70
|
+
|
|
71
|
+
// Evaluate loop policy
|
|
72
|
+
const { loadPolicy, evaluatePolicy } = await import("../core/policy.js");
|
|
73
|
+
const policy = await loadPolicy(cwd);
|
|
74
|
+
const policyResult = evaluatePolicy(policy, state, { totalCost: 0, errorCount: 0 });
|
|
75
|
+
|
|
76
|
+
const input = {
|
|
77
|
+
...hookInput,
|
|
78
|
+
last_assistant_text: lastText,
|
|
79
|
+
has_pending_stories: pending,
|
|
80
|
+
verify_result: verifyResult,
|
|
81
|
+
test_results: testResults,
|
|
82
|
+
policy_result: policyResult,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const result = evaluateStopHook(input, state);
|
|
86
|
+
|
|
87
|
+
await writeState(result.nextState, cwd);
|
|
88
|
+
await logEvents(result.events, { adapter: "auto", loop_id: state.sessionId || "unknown" }, cwd);
|
|
89
|
+
|
|
90
|
+
// Save session checkpoint (best-effort)
|
|
91
|
+
try {
|
|
92
|
+
const { saveCheckpoint } = await import("../core/session.js");
|
|
93
|
+
await saveCheckpoint(result.nextState.sessionId || `auto-${Date.now()}`, {
|
|
94
|
+
prompt: result.nextState.prompt,
|
|
95
|
+
completionPromise: result.nextState.completionPromise,
|
|
96
|
+
maxIterations: result.nextState.maxIterations,
|
|
97
|
+
currentIteration: result.nextState.currentIteration,
|
|
98
|
+
name: result.nextState.name,
|
|
99
|
+
startedAt: result.nextState.startedAt,
|
|
100
|
+
}, cwd);
|
|
101
|
+
} catch { /* best-effort */ }
|
|
102
|
+
|
|
103
|
+
if (result.message) process.stderr.write(result.message + "\n");
|
|
104
|
+
if (result.output) process.stdout.write(JSON.stringify(result.output));
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main().catch((err) => {
|
|
109
|
+
process.stderr.write(`loophaus stop-hook error: ${err.message}\n`);
|
|
110
|
+
process.exit(0);
|
|
111
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function getPackageVersion(): string;
|
|
2
|
+
export declare function isWindows(): boolean;
|
|
3
|
+
export declare function getCodexHome(): string;
|
|
4
|
+
export declare function getAgentsHome(): string;
|
|
5
|
+
export declare function getAgentsSkillsDir(): string;
|
|
6
|
+
export declare function getHooksJsonPath(): string;
|
|
7
|
+
export declare function getPluginInstallDir(): string;
|
|
8
|
+
export declare function getSkillsDir(): string;
|
|
9
|
+
export declare function getLocalCodexDir(): string;
|
|
10
|
+
export declare function getLocalPluginDir(): string;
|
|
11
|
+
export declare function getLocalHooksJsonPath(): string;
|
|
12
|
+
export declare function getLocalSkillsDir(): string;
|
|
13
|
+
export declare function getClaudeHome(): string;
|
|
14
|
+
export declare function getClaudePluginsDir(): string;
|
|
15
|
+
export declare function getClaudePluginCacheDir(version?: string): string;
|
|
16
|
+
export declare function getClaudeSettingsPath(): string;
|
|
17
|
+
export declare function getClaudeInstalledPluginsPath(): string;
|
|
18
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../lib/paths.ts"],"names":[],"mappings":"AASA,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAID,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAID,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
let _cachedVersion = null;
|
|
8
|
+
export function getPackageVersion() {
|
|
9
|
+
if (_cachedVersion)
|
|
10
|
+
return _cachedVersion;
|
|
11
|
+
try {
|
|
12
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
13
|
+
_cachedVersion = pkg.version;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
_cachedVersion = "0.0.0";
|
|
17
|
+
}
|
|
18
|
+
return _cachedVersion;
|
|
19
|
+
}
|
|
20
|
+
export function isWindows() {
|
|
21
|
+
return process.platform === "win32";
|
|
22
|
+
}
|
|
23
|
+
// --- Codex CLI paths (legacy ~/.codex + new ~/.agents) ---
|
|
24
|
+
export function getCodexHome() {
|
|
25
|
+
if (process.env.CODEX_HOME) {
|
|
26
|
+
return process.env.CODEX_HOME;
|
|
27
|
+
}
|
|
28
|
+
return join(homedir(), ".codex");
|
|
29
|
+
}
|
|
30
|
+
export function getAgentsHome() {
|
|
31
|
+
return join(homedir(), ".agents");
|
|
32
|
+
}
|
|
33
|
+
export function getAgentsSkillsDir() {
|
|
34
|
+
return join(getAgentsHome(), "skills");
|
|
35
|
+
}
|
|
36
|
+
export function getHooksJsonPath() {
|
|
37
|
+
return join(getCodexHome(), "hooks.json");
|
|
38
|
+
}
|
|
39
|
+
export function getPluginInstallDir() {
|
|
40
|
+
return join(getCodexHome(), "plugins", "loophaus");
|
|
41
|
+
}
|
|
42
|
+
export function getSkillsDir() {
|
|
43
|
+
return join(getCodexHome(), "skills");
|
|
44
|
+
}
|
|
45
|
+
export function getLocalCodexDir() {
|
|
46
|
+
return join(process.cwd(), ".codex");
|
|
47
|
+
}
|
|
48
|
+
export function getLocalPluginDir() {
|
|
49
|
+
return join(getLocalCodexDir(), "plugins", "loophaus");
|
|
50
|
+
}
|
|
51
|
+
export function getLocalHooksJsonPath() {
|
|
52
|
+
return join(getLocalCodexDir(), "hooks.json");
|
|
53
|
+
}
|
|
54
|
+
export function getLocalSkillsDir() {
|
|
55
|
+
return join(getLocalCodexDir(), "skills");
|
|
56
|
+
}
|
|
57
|
+
// --- Claude Code paths ---
|
|
58
|
+
export function getClaudeHome() {
|
|
59
|
+
return join(homedir(), ".claude");
|
|
60
|
+
}
|
|
61
|
+
export function getClaudePluginsDir() {
|
|
62
|
+
return join(getClaudeHome(), "plugins");
|
|
63
|
+
}
|
|
64
|
+
export function getClaudePluginCacheDir(version) {
|
|
65
|
+
const v = version || getPackageVersion();
|
|
66
|
+
return join(getClaudePluginsDir(), "cache", "loophaus-marketplace", "loophaus", v);
|
|
67
|
+
}
|
|
68
|
+
export function getClaudeSettingsPath() {
|
|
69
|
+
return join(getClaudeHome(), "settings.json");
|
|
70
|
+
}
|
|
71
|
+
export function getClaudeInstalledPluginsPath() {
|
|
72
|
+
return join(getClaudePluginsDir(), "installed_plugins.json");
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../lib/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { extractPromise } from "../core/engine.js";
|
|
2
|
+
import { getLastAssistantText, hasPendingStories } from "../core/io-helpers.js";
|
|
3
|
+
import type { LoopState } from "../core/types.js";
|
|
4
|
+
export { extractPromise, getLastAssistantText, hasPendingStories };
|
|
5
|
+
export interface StopHookProcessInput {
|
|
6
|
+
last_assistant_message?: string;
|
|
7
|
+
transcript_path?: string;
|
|
8
|
+
session_id?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
stop_hook_active?: boolean;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface StopHookProcessResult {
|
|
14
|
+
exitCode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function processStopHook(hookInput: StopHookProcessInput, readStateFn: () => Promise<LoopState>, writeStateFn: (state: LoopState) => Promise<void>): Promise<StopHookProcessResult>;
|
|
19
|
+
//# sourceMappingURL=stop-hook-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-hook-core.d.ts","sourceRoot":"","sources":["../../lib/stop-hook-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,SAAS,EAA4B,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,oBAAoB,EAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,EACrC,YAAY,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,GAChD,OAAO,CAAC,qBAAqB,CAAC,CAsChC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { evaluateStopHook, extractPromise } from "../core/engine.js";
|
|
2
|
+
import { getLastAssistantText, hasPendingStories } from "../core/io-helpers.js";
|
|
3
|
+
export { extractPromise, getLastAssistantText, hasPendingStories };
|
|
4
|
+
export async function processStopHook(hookInput, readStateFn, writeStateFn) {
|
|
5
|
+
const state = await readStateFn();
|
|
6
|
+
if (!state.active) {
|
|
7
|
+
return { exitCode: 0, stdout: "", stderr: "" };
|
|
8
|
+
}
|
|
9
|
+
if (state.sessionId && hookInput.session_id && state.sessionId !== hookInput.session_id) {
|
|
10
|
+
return { exitCode: 0, stdout: "", stderr: "" };
|
|
11
|
+
}
|
|
12
|
+
const lastText = hookInput.last_assistant_message ||
|
|
13
|
+
await getLastAssistantText(hookInput.transcript_path ?? undefined);
|
|
14
|
+
const pending = await hasPendingStories(hookInput.cwd || process.cwd());
|
|
15
|
+
const input = {
|
|
16
|
+
...hookInput,
|
|
17
|
+
last_assistant_text: lastText,
|
|
18
|
+
has_pending_stories: pending,
|
|
19
|
+
stop_hook_active: hookInput.stop_hook_active ?? false,
|
|
20
|
+
session_id: hookInput.session_id ?? "",
|
|
21
|
+
};
|
|
22
|
+
const result = evaluateStopHook(input, state);
|
|
23
|
+
await writeStateFn(result.nextState);
|
|
24
|
+
const stderrParts = [];
|
|
25
|
+
if (result.message)
|
|
26
|
+
stderrParts.push(result.message);
|
|
27
|
+
for (const ev of result.events || []) {
|
|
28
|
+
if (ev.event === "continue" && ev.reason === "pending_stories") {
|
|
29
|
+
stderrParts.push("Loop: stop_hook_active=true, pending stories found. Continuing.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const stderr = stderrParts.length ? stderrParts.join("\n") + "\n" : "";
|
|
33
|
+
const stdout = result.output ? JSON.stringify(result.output) : "";
|
|
34
|
+
return { exitCode: 0, stdout, stderr };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=stop-hook-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-hook-core.js","sourceRoot":"","sources":["../../lib/stop-hook-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;AAiBnE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAA+B,EAC/B,WAAqC,EACrC,YAAiD;IAEjD,MAAM,KAAK,GAAG,MAAM,WAAW,EAAE,CAAC;IAElC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACjD,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,IAAI,SAAS,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,UAAU,EAAE,CAAC;QACxF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB;QAC/C,MAAM,oBAAoB,CAAC,SAAS,CAAC,eAAe,IAAI,SAAS,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAExE,MAAM,KAAK,GAAkB;QAC3B,GAAG,SAAS;QACZ,mBAAmB,EAAE,QAAQ;QAC7B,mBAAmB,EAAE,OAAO;QAC5B,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,IAAI,KAAK;QACrD,UAAU,EAAE,SAAS,CAAC,UAAU,IAAI,EAAE;KACvC,CAAC;IAEF,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAE9C,MAAM,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAErC,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,MAAM,CAAC,OAAO;QAAE,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,IAAI,EAAiB,EAAE,CAAC;QACpD,IAAI,EAAE,CAAC,KAAK,KAAK,UAAU,IAAI,EAAE,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;YAC/D,WAAW,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAElE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graypark/loophaus",
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "loophaus — Control plane for coding agents. Iterative dev loops with multi-agent orchestration.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "graypark",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/vcz-Gray/loophaus"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/vcz-Gray/loophaus#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/vcz-Gray/loophaus/issues"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"loophaus": "./dist/bin/loophaus.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist/",
|
|
24
|
+
"hooks/",
|
|
25
|
+
"commands/",
|
|
26
|
+
"codex/",
|
|
27
|
+
"platforms/",
|
|
28
|
+
"scripts/",
|
|
29
|
+
"skills/",
|
|
30
|
+
".claude-plugin/",
|
|
31
|
+
"README.md",
|
|
32
|
+
"README.ko.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
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
|
+
"test": "vitest run",
|
|
40
|
+
"postinstall": "echo 'Run: npx loophaus --help'",
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
42
|
+
"prepare": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.0.0"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"loophaus",
|
|
49
|
+
"loop",
|
|
50
|
+
"ai-agent",
|
|
51
|
+
"iterative-development",
|
|
52
|
+
"stop-hook",
|
|
53
|
+
"control-plane",
|
|
54
|
+
"coding-agents"
|
|
55
|
+
],
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^25.5.0",
|
|
58
|
+
"typescript": "^6.0.2",
|
|
59
|
+
"vitest": "^4.1.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// platforms/claude-code/adapter.mjs
|
|
2
|
+
|
|
3
|
+
export const name = "claude-code";
|
|
4
|
+
export const platform = "claude-code";
|
|
5
|
+
|
|
6
|
+
export function parseInput(raw) {
|
|
7
|
+
const input = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
8
|
+
return {
|
|
9
|
+
session_id: input.session_id || "",
|
|
10
|
+
transcript_path: input.transcript_path || null,
|
|
11
|
+
cwd: input.cwd || process.cwd(),
|
|
12
|
+
stop_hook_active: input.stop_hook_active || false,
|
|
13
|
+
last_assistant_message: input.last_assistant_message || "",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function renderOutput(result) {
|
|
18
|
+
if (result.output) return JSON.stringify(result.output);
|
|
19
|
+
return "";
|
|
20
|
+
}
|