prloom 0.1.2 → 0.1.3
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/dist/cli/index.js +124 -15
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7238,14 +7238,46 @@ var init_execa = __esm(() => {
|
|
|
7238
7238
|
} = getIpcExport());
|
|
7239
7239
|
});
|
|
7240
7240
|
|
|
7241
|
+
// src/lib/adapters/tmux.ts
|
|
7242
|
+
async function waitForTmuxSession(sessionName) {
|
|
7243
|
+
while (true) {
|
|
7244
|
+
const { exitCode } = await execa("tmux", ["has-session", "-t", sessionName], {
|
|
7245
|
+
reject: false
|
|
7246
|
+
});
|
|
7247
|
+
if (exitCode !== 0) {
|
|
7248
|
+
return;
|
|
7249
|
+
}
|
|
7250
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1000));
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
var init_tmux = __esm(() => {
|
|
7254
|
+
init_execa();
|
|
7255
|
+
});
|
|
7256
|
+
|
|
7241
7257
|
// src/lib/adapters/codex.ts
|
|
7242
7258
|
var codexAdapter;
|
|
7243
7259
|
var init_codex = __esm(() => {
|
|
7244
7260
|
init_execa();
|
|
7261
|
+
init_tmux();
|
|
7245
7262
|
codexAdapter = {
|
|
7246
7263
|
name: "codex",
|
|
7247
|
-
async execute({ cwd, prompt }) {
|
|
7248
|
-
const
|
|
7264
|
+
async execute({ cwd, prompt, tmux }) {
|
|
7265
|
+
const args = ["exec", prompt, "--full-auto"];
|
|
7266
|
+
if (tmux) {
|
|
7267
|
+
await execa("tmux", [
|
|
7268
|
+
"new-session",
|
|
7269
|
+
"-d",
|
|
7270
|
+
"-s",
|
|
7271
|
+
tmux.sessionName,
|
|
7272
|
+
"-c",
|
|
7273
|
+
cwd,
|
|
7274
|
+
"codex",
|
|
7275
|
+
...args
|
|
7276
|
+
], { reject: false });
|
|
7277
|
+
await waitForTmuxSession(tmux.sessionName);
|
|
7278
|
+
return { exitCode: 0 };
|
|
7279
|
+
}
|
|
7280
|
+
const result = await execa("codex", args, {
|
|
7249
7281
|
cwd,
|
|
7250
7282
|
timeout: 0,
|
|
7251
7283
|
reject: false
|
|
@@ -7266,9 +7298,25 @@ var init_codex = __esm(() => {
|
|
|
7266
7298
|
var opencodeAdapter;
|
|
7267
7299
|
var init_opencode = __esm(() => {
|
|
7268
7300
|
init_execa();
|
|
7301
|
+
init_tmux();
|
|
7269
7302
|
opencodeAdapter = {
|
|
7270
7303
|
name: "opencode",
|
|
7271
|
-
async execute({ cwd, prompt }) {
|
|
7304
|
+
async execute({ cwd, prompt, tmux }) {
|
|
7305
|
+
if (tmux) {
|
|
7306
|
+
await execa("tmux", [
|
|
7307
|
+
"new-session",
|
|
7308
|
+
"-d",
|
|
7309
|
+
"-s",
|
|
7310
|
+
tmux.sessionName,
|
|
7311
|
+
"-c",
|
|
7312
|
+
cwd,
|
|
7313
|
+
"opencode",
|
|
7314
|
+
"run",
|
|
7315
|
+
prompt
|
|
7316
|
+
], { reject: false });
|
|
7317
|
+
await waitForTmuxSession(tmux.sessionName);
|
|
7318
|
+
return { exitCode: 0 };
|
|
7319
|
+
}
|
|
7272
7320
|
const result = await execa("opencode", ["run", prompt], {
|
|
7273
7321
|
cwd,
|
|
7274
7322
|
timeout: 0,
|
|
@@ -7290,10 +7338,26 @@ var init_opencode = __esm(() => {
|
|
|
7290
7338
|
var claudeAdapter;
|
|
7291
7339
|
var init_claude = __esm(() => {
|
|
7292
7340
|
init_execa();
|
|
7341
|
+
init_tmux();
|
|
7293
7342
|
claudeAdapter = {
|
|
7294
7343
|
name: "claude",
|
|
7295
|
-
async execute({ cwd, prompt }) {
|
|
7296
|
-
const
|
|
7344
|
+
async execute({ cwd, prompt, tmux }) {
|
|
7345
|
+
const args = ["-p", prompt, "--dangerously-skip-permissions"];
|
|
7346
|
+
if (tmux) {
|
|
7347
|
+
await execa("tmux", [
|
|
7348
|
+
"new-session",
|
|
7349
|
+
"-d",
|
|
7350
|
+
"-s",
|
|
7351
|
+
tmux.sessionName,
|
|
7352
|
+
"-c",
|
|
7353
|
+
cwd,
|
|
7354
|
+
"claude",
|
|
7355
|
+
...args
|
|
7356
|
+
], { reject: false });
|
|
7357
|
+
await waitForTmuxSession(tmux.sessionName);
|
|
7358
|
+
return { exitCode: 0 };
|
|
7359
|
+
}
|
|
7360
|
+
const result = await execa("claude", args, {
|
|
7297
7361
|
cwd,
|
|
7298
7362
|
timeout: 0,
|
|
7299
7363
|
reject: false
|
|
@@ -7314,7 +7378,7 @@ var manualAdapter;
|
|
|
7314
7378
|
var init_manual = __esm(() => {
|
|
7315
7379
|
manualAdapter = {
|
|
7316
7380
|
name: "manual",
|
|
7317
|
-
async execute({ cwd, prompt }) {
|
|
7381
|
+
async execute({ cwd, prompt, tmux }) {
|
|
7318
7382
|
console.log("⚠️ Manual agent: execute() called but should be skipped by dispatcher.");
|
|
7319
7383
|
console.log(" This plan is intended for IDE-driven execution.");
|
|
7320
7384
|
return { exitCode: 0 };
|
|
@@ -17582,7 +17646,7 @@ __export(exports_dispatcher, {
|
|
|
17582
17646
|
});
|
|
17583
17647
|
import { join as join9 } from "path";
|
|
17584
17648
|
import { existsSync as existsSync9, statSync as statSync5 } from "fs";
|
|
17585
|
-
async function runDispatcher(repoRoot) {
|
|
17649
|
+
async function runDispatcher(repoRoot, options2 = {}) {
|
|
17586
17650
|
const config = loadConfig(repoRoot);
|
|
17587
17651
|
const worktreesDir = resolveWorktreesDir(repoRoot, config);
|
|
17588
17652
|
acquireLock(repoRoot);
|
|
@@ -17610,7 +17674,7 @@ async function runDispatcher(repoRoot) {
|
|
|
17610
17674
|
await handleCommand2(state, cmd);
|
|
17611
17675
|
}
|
|
17612
17676
|
await ingestInboxPlans(repoRoot, worktreesDir, config, state);
|
|
17613
|
-
await processActivePlans(repoRoot, config, state, botLogin);
|
|
17677
|
+
await processActivePlans(repoRoot, config, state, botLogin, options2);
|
|
17614
17678
|
saveState(repoRoot, state);
|
|
17615
17679
|
await sleepUntilIpcOrTimeout(repoRoot, state.control_cursor, config.poll_interval_ms);
|
|
17616
17680
|
} catch (error) {
|
|
@@ -17667,7 +17731,7 @@ function getFeedbackPollDecision(opts) {
|
|
|
17667
17731
|
shouldUpdateLastPolledAt: !pollOnce && shouldPoll
|
|
17668
17732
|
};
|
|
17669
17733
|
}
|
|
17670
|
-
async function processActivePlans(repoRoot, config, state, botLogin) {
|
|
17734
|
+
async function processActivePlans(repoRoot, config, state, botLogin, options2 = {}) {
|
|
17671
17735
|
for (const [planId, ps] of Object.entries(state.plans)) {
|
|
17672
17736
|
try {
|
|
17673
17737
|
const planPath = join9(ps.worktree, ps.planRelpath);
|
|
@@ -17702,7 +17766,7 @@ async function processActivePlans(repoRoot, config, state, botLogin) {
|
|
|
17702
17766
|
if (newFeedback.length > 0) {
|
|
17703
17767
|
console.log(`\uD83D\uDCAC ${newFeedback.length} new feedback for ${planId}`);
|
|
17704
17768
|
if (!isManualAgent) {
|
|
17705
|
-
await runTriage(repoRoot, config, ps, plan, newFeedback);
|
|
17769
|
+
await runTriage(repoRoot, config, ps, plan, newFeedback, options2);
|
|
17706
17770
|
}
|
|
17707
17771
|
plan = parsePlan(planPath);
|
|
17708
17772
|
const maxIds = getMaxFeedbackIds(newFeedback);
|
|
@@ -17725,7 +17789,15 @@ async function processActivePlans(repoRoot, config, state, botLogin) {
|
|
|
17725
17789
|
const prompt = renderWorkerPrompt(repoRoot, plan, todo);
|
|
17726
17790
|
const agentName = plan.frontmatter.agent ?? config.agents.default;
|
|
17727
17791
|
const adapter = getAdapter(agentName);
|
|
17728
|
-
|
|
17792
|
+
const tmuxConfig = options2.tmux ? { sessionName: `prloom-${planId}` } : undefined;
|
|
17793
|
+
if (tmuxConfig) {
|
|
17794
|
+
ps.tmuxSession = tmuxConfig.sessionName;
|
|
17795
|
+
console.log(` [spawned in tmux session: ${tmuxConfig.sessionName}]`);
|
|
17796
|
+
}
|
|
17797
|
+
await adapter.execute({ cwd: ps.worktree, prompt, tmux: tmuxConfig });
|
|
17798
|
+
if (tmuxConfig) {
|
|
17799
|
+
ps.tmuxSession = undefined;
|
|
17800
|
+
}
|
|
17729
17801
|
const committed = await commitAll(ps.worktree, `[prloom] ${planId}: TODO #${todo.index}`);
|
|
17730
17802
|
if (committed) {
|
|
17731
17803
|
await push(ps.worktree, ps.branch);
|
|
@@ -17773,13 +17845,14 @@ async function pollNewFeedback(repoRoot, ps, botLogin) {
|
|
|
17773
17845
|
};
|
|
17774
17846
|
return filterNewFeedback(allFeedback, cursors, botLogin);
|
|
17775
17847
|
}
|
|
17776
|
-
async function runTriage(repoRoot, config, ps, plan, feedback) {
|
|
17848
|
+
async function runTriage(repoRoot, config, ps, plan, feedback, options2 = {}) {
|
|
17777
17849
|
ensureWorktreePrloomDir(ps.worktree);
|
|
17778
17850
|
const triageAgent = config.agents.designer ?? config.agents.default;
|
|
17779
17851
|
const adapter = getAdapter(triageAgent);
|
|
17780
17852
|
const prompt = renderTriagePrompt(repoRoot, plan, feedback);
|
|
17781
17853
|
console.log(`\uD83D\uDD0D Running triage for ${plan.frontmatter.id}...`);
|
|
17782
|
-
|
|
17854
|
+
const tmuxConfig = options2.tmux ? { sessionName: `prloom-triage-${plan.frontmatter.id}` } : undefined;
|
|
17855
|
+
await adapter.execute({ cwd: ps.worktree, prompt, tmux: tmuxConfig });
|
|
17783
17856
|
try {
|
|
17784
17857
|
const result = readTriageResultFile(ps.worktree);
|
|
17785
17858
|
if (result.rebase_requested) {
|
|
@@ -17993,6 +18066,35 @@ var init_open = __esm(() => {
|
|
|
17993
18066
|
init_adapters();
|
|
17994
18067
|
});
|
|
17995
18068
|
|
|
18069
|
+
// src/cli/watch.ts
|
|
18070
|
+
var exports_watch = {};
|
|
18071
|
+
__export(exports_watch, {
|
|
18072
|
+
runWatch: () => runWatch
|
|
18073
|
+
});
|
|
18074
|
+
async function runWatch(repoRoot, planId) {
|
|
18075
|
+
const state = loadState(repoRoot);
|
|
18076
|
+
const ps = state.plans[planId];
|
|
18077
|
+
if (!ps) {
|
|
18078
|
+
console.error(`Plan not found in state: ${planId}`);
|
|
18079
|
+
console.error("Make sure the plan has been dispatched at least once.");
|
|
18080
|
+
process.exit(1);
|
|
18081
|
+
}
|
|
18082
|
+
if (!ps.tmuxSession) {
|
|
18083
|
+
console.error(`No active tmux session for ${planId}`);
|
|
18084
|
+
console.error("This plan may not be running, or dispatcher wasn't started with --tmux");
|
|
18085
|
+
process.exit(1);
|
|
18086
|
+
}
|
|
18087
|
+
console.log(`Attaching to ${ps.tmuxSession} (read-only)...`);
|
|
18088
|
+
console.log("Press Ctrl+B D to detach without interrupting the worker.");
|
|
18089
|
+
await execa("tmux", ["attach", "-t", ps.tmuxSession, "-r"], {
|
|
18090
|
+
stdio: "inherit"
|
|
18091
|
+
});
|
|
18092
|
+
}
|
|
18093
|
+
var init_watch = __esm(() => {
|
|
18094
|
+
init_execa();
|
|
18095
|
+
init_state();
|
|
18096
|
+
});
|
|
18097
|
+
|
|
17996
18098
|
// src/cli/logs.ts
|
|
17997
18099
|
var exports_logs = {};
|
|
17998
18100
|
__export(exports_logs, {
|
|
@@ -23508,9 +23610,13 @@ yargs_default(hideBin(process.argv)).scriptName("prloom").usage("$0 <command> [o
|
|
|
23508
23610
|
}), async (argv) => {
|
|
23509
23611
|
const { runEdit: runEdit2 } = await Promise.resolve().then(() => (init_edit(), exports_edit));
|
|
23510
23612
|
await runEdit2(await getRepoRoot(), argv["plan-id"], argv.agent, argv["no-designer"]);
|
|
23511
|
-
}).command("start", "Start the dispatcher", () =>
|
|
23613
|
+
}).command("start", "Start the dispatcher", (yargs) => yargs.option("tmux", {
|
|
23614
|
+
type: "boolean",
|
|
23615
|
+
describe: "Run workers in tmux sessions for observation",
|
|
23616
|
+
default: false
|
|
23617
|
+
}), async (argv) => {
|
|
23512
23618
|
const { runDispatcher: runDispatcher2 } = await Promise.resolve().then(() => (init_dispatcher(), exports_dispatcher));
|
|
23513
|
-
await runDispatcher2(await getRepoRoot());
|
|
23619
|
+
await runDispatcher2(await getRepoRoot(), { tmux: argv.tmux });
|
|
23514
23620
|
}).command("status", "Show plan states", () => {}, async () => {
|
|
23515
23621
|
const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
|
|
23516
23622
|
await runStatus2(await getRepoRoot());
|
|
@@ -23523,6 +23629,9 @@ yargs_default(hideBin(process.argv)).scriptName("prloom").usage("$0 <command> [o
|
|
|
23523
23629
|
}).command("open <plan-id>", "Open TUI for manual work (requires paused)", (yargs) => yargs.positional("plan-id", { type: "string", demandOption: true }), async (argv) => {
|
|
23524
23630
|
const { runOpen: runOpen2 } = await Promise.resolve().then(() => (init_open(), exports_open));
|
|
23525
23631
|
await runOpen2(await getRepoRoot(), argv["plan-id"]);
|
|
23632
|
+
}).command("watch <plan-id>", "Observe a running worker (requires --tmux mode)", (yargs) => yargs.positional("plan-id", { type: "string", demandOption: true }), async (argv) => {
|
|
23633
|
+
const { runWatch: runWatch2 } = await Promise.resolve().then(() => (init_watch(), exports_watch));
|
|
23634
|
+
await runWatch2(await getRepoRoot(), argv["plan-id"]);
|
|
23526
23635
|
}).command("logs <plan-id>", "Show session ID for a plan", (yargs) => yargs.positional("plan-id", { type: "string", demandOption: true }), async (argv) => {
|
|
23527
23636
|
const { runLogs: runLogs2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
|
|
23528
23637
|
await runLogs2(await getRepoRoot(), argv["plan-id"]);
|