prloom 0.1.3 → 0.1.4

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.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  You write a plan (a Markdown checklist), `prloom` turns it into a dedicated git worktree + branch, opens a draft PR, and then iterates one TODO at a time using a configurable coding agent. Review happens in GitHub: comments and review submissions are triaged into new TODOs and pushed back onto the same PR.
6
6
 
7
- `prloom` is designed to be safe to run from multiple clones: all runtime state lives in `.prloom/` (gitignored), so each developer can run their own dispatcher against the PRs they create/track in their local state.
7
+ `prloom` is designed to be safe to run from multiple clones: all runtime state lives in `prloom/.local/` (gitignored), so each developer can run their own dispatcher against the PRs they create/track in their local state.
8
8
 
9
9
  ## How It Works
10
10
 
11
- - Plans start locally in `.prloom/inbox/` (gitignored; clean `git status`).
12
- - The dispatcher ingests a plan into a new branch/worktree at `plans/<id>.md` and opens a draft PR.
11
+ - Plans start locally in `prloom/.local/inbox/` (gitignored; clean `git status`).
12
+ - The dispatcher ingests a plan into a new branch/worktree at `prloom/plans/<id>.md` and opens a draft PR.
13
13
  - The worker agent executes exactly one TODO per iteration and updates the plan in-branch.
14
14
  - PR comments/reviews trigger a triage agent which updates the plan with new TODOs and posts a reply.
15
15
  - When all TODOs are complete, the PR is marked ready; you merge when satisfied.
@@ -88,13 +88,13 @@ bun run build
88
88
  - `npx -y prloom new my-feature`
89
89
  3. Start the dispatcher:
90
90
  - `npx -y prloom start`
91
- 3. Review the draft PR in GitHub.
92
- 4. Leave PR comments or a review; `prloom` triages feedback into TODOs.
93
- 5. When the PR is ready, merge it.
91
+ 4. Review the draft PR in GitHub.
92
+ 5. Leave PR comments or a review; `prloom` triages feedback into TODOs.
93
+ 6. When the PR is ready, merge it.
94
94
 
95
95
  ## Configuration
96
96
 
97
- Create `prloom.config.json` in the repo root:
97
+ Create `prloom/config.json`:
98
98
 
99
99
  ```json
100
100
  {
@@ -102,13 +102,32 @@ Create `prloom.config.json` in the repo root:
102
102
  "default": "opencode",
103
103
  "designer": "codex"
104
104
  },
105
- "worktrees_dir": ".prloom/worktrees",
105
+ "worktrees_dir": "prloom/.local/worktrees",
106
106
  "poll_interval_ms": 60000,
107
107
  "base_branch": "main"
108
108
  }
109
109
  ```
110
110
 
111
+ ## Repository Context
112
+
113
+ You can provide repository-specific context to agents by creating markdown files in the `prloom/` directory:
114
+
115
+ ```
116
+ repo/
117
+ ├── prloom/
118
+ │ ├── config.json # Configuration
119
+ │ ├── plans/ # Committed plans (on PR branches)
120
+ │ ├── planner.md # Appended to designer prompts
121
+ │ ├── worker.md # Appended to worker prompts
122
+ │ └── .local/ # Gitignored (runtime state)
123
+ ```
124
+
125
+ - **`prloom/planner.md`**: Architecture info, coding conventions, design patterns
126
+ - **`prloom/worker.md`**: Build commands, test patterns, implementation guidelines
127
+
128
+ These files are appended to the respective agent prompts automatically.
129
+
111
130
  ## Notes
112
131
 
113
- - Runtime state is stored under `.prloom/` (gitignored).
114
- - The plan file is committed on the PR branch at `plans/<id>.md` and lands on the configured `base_branch` only when you merge the PR.
132
+ - Runtime state is stored under `prloom/.local/` (gitignored).
133
+ - The plan file is committed on the PR branch at `prloom/plans/<id>.md` and lands on the configured `base_branch` when you merge the PR.
package/dist/cli/index.js CHANGED
@@ -7421,7 +7421,7 @@ var init_adapters = __esm(() => {
7421
7421
  import { join as join2, resolve as resolve5 } from "path";
7422
7422
  import { existsSync, readFileSync as readFileSync6 } from "fs";
7423
7423
  function loadConfig(repoRoot) {
7424
- const configPath = join2(repoRoot, "prloom.config.json");
7424
+ const configPath = join2(repoRoot, "prloom", "config.json");
7425
7425
  if (!existsSync(configPath)) {
7426
7426
  return { ...DEFAULTS };
7427
7427
  }
@@ -7458,7 +7458,7 @@ var init_config = __esm(() => {
7458
7458
  agents: {
7459
7459
  default: "opencode"
7460
7460
  },
7461
- worktrees_dir: ".prloom/worktrees",
7461
+ worktrees_dir: "prloom/.local/worktrees",
7462
7462
  poll_interval_ms: 60000,
7463
7463
  base_branch: "main"
7464
7464
  };
@@ -7479,7 +7479,9 @@ async function runInit(cwd, opts = {}) {
7479
7479
  await ensureGhAuthed();
7480
7480
  await ensureGhRepoResolvable(repoRoot);
7481
7481
  const defaultBranch = await detectDefaultBranch(repoRoot);
7482
- const configPath = join3(repoRoot, "prloom.config.json");
7482
+ const configPath = join3(repoRoot, "prloom", "config.json");
7483
+ const prloomDir = join3(repoRoot, "prloom");
7484
+ mkdirSync(prloomDir, { recursive: true });
7483
7485
  if (!existsSync2(configPath) || opts.force) {
7484
7486
  const existing = loadConfig(repoRoot);
7485
7487
  const config = {
@@ -7494,12 +7496,14 @@ async function runInit(cwd, opts = {}) {
7494
7496
  } else {
7495
7497
  console.log(`Found existing ${configPath} (leaving unchanged)`);
7496
7498
  }
7497
- const prloomDir = join3(repoRoot, ".prloom");
7498
- const inboxDir = join3(prloomDir, "inbox");
7499
- const plansDir = join3(prloomDir, "plans");
7499
+ const localDir = join3(prloomDir, ".local");
7500
+ const inboxDir = join3(localDir, "inbox");
7501
+ const plansStateDir = join3(localDir, "plans");
7500
7502
  mkdirSync(inboxDir, { recursive: true });
7503
+ mkdirSync(plansStateDir, { recursive: true });
7504
+ const plansDir = join3(prloomDir, "plans");
7501
7505
  mkdirSync(plansDir, { recursive: true });
7502
- await ensureGitignoreEntry(repoRoot, ".prloom/");
7506
+ await ensureGitignoreEntry(repoRoot, "prloom/.local/");
7503
7507
  console.log("✅ prloom initialized");
7504
7508
  console.log(`Base branch: ${defaultBranch}`);
7505
7509
  if (!opts.yes) {
@@ -11202,7 +11206,7 @@ ${plan.progressLog}`;
11202
11206
  function generatePlanSkeleton(id, agent, baseBranch) {
11203
11207
  const frontmatter = {
11204
11208
  id,
11205
- status: "queued"
11209
+ status: "draft"
11206
11210
  };
11207
11211
  if (agent) {
11208
11212
  frontmatter.agent = agent;
@@ -16898,11 +16902,10 @@ You are implementing exactly ONE task from this plan.
16898
16902
 
16899
16903
  You are processing PR feedback for an active plan. Your job is to:
16900
16904
 
16901
- 1. Analyze all review feedback (comments, reviews, inline comments)
16902
- 2. Create specific, actionable TODO items for the plan
16903
- 3. Group related feedback into fewer tasks where appropriate
16905
+ 1. **Classify** each feedback item by type
16906
+ 2. **Respond** appropriately based on the type
16907
+ 3. **Update** the plan if changes are needed
16904
16908
  4. Detect if a rebase was requested
16905
- 5. Compose a reply message for the reviewer(s)
16906
16909
 
16907
16910
  ## Feedback to Process
16908
16911
 
@@ -16912,9 +16915,36 @@ You are processing PR feedback for an active plan. Your job is to:
16912
16915
 
16913
16916
  {{plan}}
16914
16917
 
16915
- ## Instructions
16918
+ ---
16919
+
16920
+ ## Step 1: Classify Each Feedback Item
16921
+
16922
+ Before acting, identify the type of each feedback item:
16923
+
16924
+ | Type | Description | Action |
16925
+ | ------------------- | --------------------------------------------- | ------------------------ |
16926
+ | **Question** | Asking "why", "how", or seeking clarification | Answer in reply, NO TODO |
16927
+ | **Change Request** | Asking for code modifications | Create specific TODO |
16928
+ | **Approval/Praise** | Positive feedback, LGTM, approval | Acknowledge briefly |
16929
+ | **Process Request** | Rebase, update branch, etc. | Set flag, acknowledge |
16930
+
16931
+ ## Step 2: Handle Each Type
16932
+
16933
+ ### Questions → Answer Directly
16934
+
16935
+ For questions, **answer them substantively in your reply**:
16916
16936
 
16917
- ### Adding TODOs
16937
+ - Explore the codebase to find the answer if needed
16938
+ - Explain the reasoning behind implementation decisions
16939
+ - Reference specific code locations or commits if helpful
16940
+ - Do NOT create a TODO like "answer the question" - just answer it
16941
+
16942
+ Example question: "Why did you use a polling approach instead of webhooks?"
16943
+ → Reply with the actual reasoning, don't create a TODO.
16944
+
16945
+ ### Change Requests → Create TODOs
16946
+
16947
+ For explicit requests to modify code:
16918
16948
 
16919
16949
  - Edit the plan file directly to add actionable TODOs
16920
16950
  - Add them at the end of the ## TODO section
@@ -16924,13 +16954,23 @@ You are processing PR feedback for an active plan. Your job is to:
16924
16954
  - "Update function X to handle null input"
16925
16955
  - "Add test for Y edge case"
16926
16956
  - "Rename Z for clarity"
16927
- - "Remove deprecated code in file A"
16928
- - Group related comments into single tasks when logical
16957
+ - Group related requests into single tasks when logical
16929
16958
  - If the plan status is \`done\` and you add TODOs, change status to \`active\`
16930
16959
 
16931
- ### Writing the Result File
16960
+ ### Approval/Praise Acknowledge
16961
+
16962
+ Simply thank the reviewer in your reply. No TODO needed.
16963
+
16964
+ ### Process Requests → Set Flag
16932
16965
 
16933
- After editing the plan, you MUST write \`.prloom/triage-result.json\` with:
16966
+ If any comment mentions rebase, update branch, or similar:
16967
+
16968
+ - Set \`rebase_requested: true\` in the result file
16969
+ - Acknowledge in your reply
16970
+
16971
+ ## Step 3: Write the Result File
16972
+
16973
+ After processing, you MUST write \`prloom/.local/triage-result.json\`:
16934
16974
 
16935
16975
  \`\`\`json
16936
16976
  {
@@ -16941,22 +16981,23 @@ After editing the plan, you MUST write \`.prloom/triage-result.json\` with:
16941
16981
 
16942
16982
  **Required fields:**
16943
16983
 
16944
- - \`reply_markdown\`: Your response to post on the PR (ALWAYS required, even for no-ops)
16945
- - \`rebase_requested\`: Set to \`true\` if any comment mentions rebase, update branch, or similar
16984
+ - \`reply_markdown\`: Your response to post on the PR (ALWAYS required)
16985
+ - \`rebase_requested\`: Set to \`true\` if rebase was requested
16946
16986
 
16947
16987
  ### Reply Guidelines
16948
16988
 
16949
16989
  - Be polite and professional
16950
- - Acknowledge specific feedback points
16951
- - Explain what TODOs were created
16952
- - If no changes needed, explain why
16953
- - Keep it concise
16990
+ - **Answer questions directly** - this is the most important part
16991
+ - Explain what TODOs were created (if any)
16992
+ - If a request doesn't require changes, explain why
16993
+ - Keep it concise but complete
16954
16994
 
16955
16995
  ## Critical Rules
16956
16996
 
16957
- 1. You MUST write \`.prloom/triage-result.json\` even if you add no TODOs
16997
+ 1. You MUST write \`prloom/.local/triage-result.json\` even if you add no TODOs
16958
16998
  2. The result file must contain valid JSON only, no markdown wrapper
16959
16999
  3. Failure to write the result file will mark the plan as blocked
17000
+ 4. **Questions should be answered, not converted to TODOs**
16960
17001
  `
16961
17002
  };
16962
17003
  });
@@ -16967,32 +17008,72 @@ import { join as join4 } from "path";
16967
17008
  function loadTemplate(_repoRoot, name) {
16968
17009
  return BUILTIN_PROMPTS[name];
16969
17010
  }
17011
+ function loadAgentContext(repoRoot, agentType) {
17012
+ const contextPath = join4(repoRoot, "prloom", `${agentType}.md`);
17013
+ if (!existsSync3(contextPath)) {
17014
+ return "";
17015
+ }
17016
+ return readFileSync9(contextPath, "utf-8");
17017
+ }
16970
17018
  function renderWorkerPrompt(repoRoot, plan, todo) {
16971
17019
  const template = loadTemplate(repoRoot, "worker");
16972
17020
  const compiled = import_handlebars.default.compile(template);
16973
- return compiled({
17021
+ let prompt = compiled({
16974
17022
  current_todo: `TODO #${todo.index}: ${todo.text}`,
16975
17023
  plan: plan.raw
16976
17024
  });
17025
+ const context = loadAgentContext(repoRoot, "worker");
17026
+ if (context) {
17027
+ prompt += `
17028
+
17029
+ ---
17030
+
17031
+ # Repository Context
17032
+
17033
+ ${context}`;
17034
+ }
17035
+ return prompt;
16977
17036
  }
16978
17037
  function renderDesignerNewPrompt(repoPath, planPath, baseBranch, workerAgent, userDescription) {
16979
17038
  const template = BUILTIN_PROMPTS["designer_new"];
16980
17039
  const compiled = import_handlebars.default.compile(template);
16981
- return compiled({
17040
+ let prompt = compiled({
16982
17041
  repo_path: repoPath,
16983
17042
  plan_path: planPath,
16984
17043
  base_branch: baseBranch,
16985
17044
  worker_agent: workerAgent,
16986
17045
  user_description: userDescription ?? ""
16987
17046
  });
17047
+ const context = loadAgentContext(repoPath, "planner");
17048
+ if (context) {
17049
+ prompt += `
17050
+
17051
+ ---
17052
+
17053
+ # Repository Context
17054
+
17055
+ ${context}`;
17056
+ }
17057
+ return prompt;
16988
17058
  }
16989
- function renderDesignerEditPrompt(planPath, existingPlan) {
17059
+ function renderDesignerEditPrompt(repoPath, planPath, existingPlan) {
16990
17060
  const template = BUILTIN_PROMPTS["designer_edit"];
16991
17061
  const compiled = import_handlebars.default.compile(template);
16992
- return compiled({
17062
+ let prompt = compiled({
16993
17063
  plan_path: planPath,
16994
17064
  existing_plan: existingPlan
16995
17065
  });
17066
+ const context = loadAgentContext(repoPath, "planner");
17067
+ if (context) {
17068
+ prompt += `
17069
+
17070
+ ---
17071
+
17072
+ # Repository Context
17073
+
17074
+ ${context}`;
17075
+ }
17076
+ return prompt;
16996
17077
  }
16997
17078
  function renderTriagePrompt(repoRoot, plan, feedback) {
16998
17079
  const template = loadTemplate(repoRoot, "review_triage");
@@ -17008,6 +17089,10 @@ ${f.body}`;
17008
17089
  entry += `
17009
17090
 
17010
17091
  *Review: ${f.reviewState}*`;
17092
+ if (f.inReplyToId)
17093
+ entry += `
17094
+
17095
+ *In reply to comment #${f.inReplyToId}*`;
17011
17096
  return entry;
17012
17097
  }).join(`
17013
17098
 
@@ -17039,7 +17124,7 @@ function readTriageResultFile(worktreePath) {
17039
17124
  rebase_requested: result.rebase_requested
17040
17125
  };
17041
17126
  }
17042
- var import_handlebars, TRIAGE_RESULT_FILE = ".prloom/triage-result.json";
17127
+ var import_handlebars, TRIAGE_RESULT_FILE = "prloom/.local/triage-result.json";
17043
17128
  var init_template2 = __esm(() => {
17044
17129
  init_prompt_sources();
17045
17130
  import_handlebars = __toESM(require_lib(), 1);
@@ -17174,7 +17259,7 @@ function deleteInboxPlan(repoRoot, planId) {
17174
17259
  unlinkSync2(inboxPath);
17175
17260
  }
17176
17261
  }
17177
- var SWARM_DIR = ".prloom", STATE_FILE = "state.json", LOCK_FILE = "lock", PLANS_DIR = "plans", INBOX_DIR = "inbox";
17262
+ var SWARM_DIR = "prloom/.local", STATE_FILE = "state.json", LOCK_FILE = "lock", PLANS_DIR = "plans", INBOX_DIR = "inbox";
17178
17263
  var init_state = () => {};
17179
17264
 
17180
17265
  // node_modules/nanoid/url-alphabet/index.js
@@ -17301,7 +17386,7 @@ function copyFileToWorktree(srcPath, worktreePath, destRelPath) {
17301
17386
  copyFileSync(srcPath, destPath);
17302
17387
  }
17303
17388
  function ensureWorktreePrloomDir(worktreePath) {
17304
- const prloomDir = join6(worktreePath, ".prloom");
17389
+ const prloomDir = join6(worktreePath, "prloom", ".local");
17305
17390
  if (!existsSync5(prloomDir)) {
17306
17391
  mkdirSync3(prloomDir, { recursive: true });
17307
17392
  }
@@ -17311,6 +17396,23 @@ var init_git = __esm(() => {
17311
17396
  init_nanoid();
17312
17397
  });
17313
17398
 
17399
+ // src/cli/prompt.ts
17400
+ import * as readline from "readline";
17401
+ function confirm(message) {
17402
+ const rl = readline.createInterface({
17403
+ input: process.stdin,
17404
+ output: process.stdout
17405
+ });
17406
+ return new Promise((resolve6) => {
17407
+ rl.question(`${message} (y/N) `, (answer) => {
17408
+ rl.close();
17409
+ const normalized = answer.trim().toLowerCase();
17410
+ resolve6(normalized === "y" || normalized === "yes");
17411
+ });
17412
+ });
17413
+ }
17414
+ var init_prompt = () => {};
17415
+
17314
17416
  // src/cli/new.ts
17315
17417
  var exports_new = {};
17316
17418
  __export(exports_new, {
@@ -17348,8 +17450,8 @@ async function runNew(repoRoot, planId, agentOverride, noDesigner) {
17348
17450
  console.log(`Worker agent: ${workerAgent}`);
17349
17451
  if (noDesigner) {
17350
17452
  console.log("");
17351
- console.log("Plan skeleton created. Edit manually or use your IDE.");
17352
- console.log("Run 'prloom start' to dispatch when ready.");
17453
+ console.log("Plan skeleton created (status: draft).");
17454
+ console.log("Use 'prloom edit' to design, then queue for dispatch.");
17353
17455
  return;
17354
17456
  }
17355
17457
  const adapter = getAdapter(designerAgent);
@@ -17358,8 +17460,15 @@ async function runNew(repoRoot, planId, agentOverride, noDesigner) {
17358
17460
  console.log("Starting Designer session to fill in the plan...");
17359
17461
  const prompt = renderDesignerNewPrompt(repoRoot, planPath, baseBranch, workerAgent);
17360
17462
  await adapter.interactive({ cwd: repoRoot, prompt });
17463
+ console.log("");
17361
17464
  console.log("Designer session ended.");
17362
- console.log("Plan is now in inbox. Run 'prloom start' to dispatch.");
17465
+ const shouldQueue = await confirm("Queue this plan for the dispatcher?");
17466
+ if (shouldQueue) {
17467
+ setStatus(planPath, "queued");
17468
+ console.log("Plan queued. Run 'prloom start' to dispatch.");
17469
+ } else {
17470
+ console.log("Plan left as draft. Use 'prloom edit' to continue later.");
17471
+ }
17363
17472
  }
17364
17473
  var init_new = __esm(() => {
17365
17474
  init_config();
@@ -17368,6 +17477,7 @@ var init_new = __esm(() => {
17368
17477
  init_template2();
17369
17478
  init_state();
17370
17479
  init_git();
17480
+ init_prompt();
17371
17481
  });
17372
17482
 
17373
17483
  // src/cli/edit.ts
@@ -17383,9 +17493,11 @@ async function runEdit(repoRoot, planId, agentOverride, noDesigner) {
17383
17493
  const inboxPath = getInboxPath(repoRoot, planId);
17384
17494
  let planPath;
17385
17495
  let cwd;
17496
+ let isInbox = false;
17386
17497
  if (existsSync7(inboxPath)) {
17387
17498
  planPath = inboxPath;
17388
17499
  cwd = repoRoot;
17500
+ isInbox = true;
17389
17501
  console.log(`Editing inbox plan: ${planId}`);
17390
17502
  } else {
17391
17503
  const ps = state.plans[planId];
@@ -17413,15 +17525,30 @@ async function runEdit(repoRoot, planId, agentOverride, noDesigner) {
17413
17525
  const agentName = agentOverride ?? config.agents.designer ?? config.agents.default;
17414
17526
  const adapter = getAdapter(agentName);
17415
17527
  console.log(`Agent: ${agentName}`);
17416
- const prompt = renderDesignerEditPrompt(planPath, existingPlan);
17528
+ const prompt = renderDesignerEditPrompt(cwd, planPath, existingPlan);
17417
17529
  await adapter.interactive({ cwd, prompt });
17530
+ console.log("");
17418
17531
  console.log("Designer session ended.");
17532
+ if (isInbox) {
17533
+ const plan = parsePlan(planPath);
17534
+ if (plan.frontmatter.status === "draft") {
17535
+ const shouldQueue = await confirm("Queue this plan for the dispatcher?");
17536
+ if (shouldQueue) {
17537
+ setStatus(planPath, "queued");
17538
+ console.log("Plan queued. Run 'prloom start' to dispatch.");
17539
+ } else {
17540
+ console.log("Plan left as draft.");
17541
+ }
17542
+ }
17543
+ }
17419
17544
  }
17420
17545
  var init_edit = __esm(() => {
17421
17546
  init_config();
17422
17547
  init_adapters();
17423
17548
  init_template2();
17424
17549
  init_state();
17550
+ init_plan();
17551
+ init_prompt();
17425
17552
  });
17426
17553
 
17427
17554
  // src/lib/ipc.ts
@@ -17439,10 +17566,10 @@ import {
17439
17566
  mkdirSync as mkdirSync4
17440
17567
  } from "fs";
17441
17568
  function getControlPath(repoRoot) {
17442
- return join8(repoRoot, ".prloom", CONTROL_FILE);
17569
+ return join8(repoRoot, "prloom", ".local", CONTROL_FILE);
17443
17570
  }
17444
17571
  function enqueue(repoRoot, cmd) {
17445
- const prloomDir = join8(repoRoot, ".prloom");
17572
+ const prloomDir = join8(repoRoot, "prloom", ".local");
17446
17573
  if (!existsSync8(prloomDir)) {
17447
17574
  mkdirSync4(prloomDir, { recursive: true });
17448
17575
  }
@@ -17572,7 +17699,7 @@ async function getPRReviewComments(repoRoot, prNumber) {
17572
17699
  "api",
17573
17700
  `repos/{owner}/{repo}/pulls/${prNumber}/comments`,
17574
17701
  "--jq",
17575
- ".[] | {id: .id, author: .user.login, body: .body, path: .path, line: .line, createdAt: .created_at}"
17702
+ ".[] | {id: .id, author: .user.login, body: .body, path: .path, line: .line, createdAt: .created_at, inReplyToId: .in_reply_to_id}"
17576
17703
  ], { cwd: repoRoot });
17577
17704
  if (!stdout.trim())
17578
17705
  return [];
@@ -17586,7 +17713,8 @@ async function getPRReviewComments(repoRoot, prNumber) {
17586
17713
  body: obj.body,
17587
17714
  path: obj.path,
17588
17715
  line: obj.line,
17589
- createdAt: obj.createdAt
17716
+ createdAt: obj.createdAt,
17717
+ inReplyToId: obj.inReplyToId || undefined
17590
17718
  };
17591
17719
  });
17592
17720
  }
@@ -17693,11 +17821,14 @@ async function ingestInboxPlans(repoRoot, worktreesDir, config, state) {
17693
17821
  console.error(`ID mismatch: file ${planId}.md has id: ${plan.frontmatter.id}, skipping`);
17694
17822
  continue;
17695
17823
  }
17824
+ if (plan.frontmatter.status === "draft") {
17825
+ continue;
17826
+ }
17696
17827
  console.log(`\uD83D\uDCE5 Ingesting inbox plan: ${planId}`);
17697
17828
  const baseBranch = plan.frontmatter.base_branch ?? config.base_branch;
17698
17829
  const branch = await createBranchName(planId);
17699
17830
  const worktreePath = await createWorktree(repoRoot, worktreesDir, branch, baseBranch);
17700
- const planRelpath = `plans/${planId}.md`;
17831
+ const planRelpath = `prloom/plans/${planId}.md`;
17701
17832
  copyFileToWorktree(inboxPath, worktreePath, planRelpath);
17702
17833
  const worktreePlanPath = join9(worktreePath, planRelpath);
17703
17834
  setStatus(worktreePlanPath, "active");
@@ -17862,12 +17993,44 @@ async function runTriage(repoRoot, config, ps, plan, feedback, options2 = {}) {
17862
17993
  const planPath = join9(ps.worktree, ps.planRelpath);
17863
17994
  setStatus(planPath, "blocked");
17864
17995
  ps.lastError = `Rebase conflict: ${rebaseResult.conflictFiles?.join(", ")}`;
17865
- await postPRComment(repoRoot, ps.pr, `⚠️ Rebase conflict detected:
17996
+ await postPRComment(repoRoot, ps.pr, `⚠️ **Rebase conflict detected**
17997
+
17998
+ The following files have conflicts:
17866
17999
  \`\`\`
17867
18000
  ${rebaseResult.conflictFiles?.join(`
17868
18001
  `)}
17869
18002
  \`\`\`
17870
- Please resolve manually.`);
18003
+
18004
+ **To resolve:**
18005
+
18006
+ 1. Navigate to the worktree:
18007
+ \`\`\`
18008
+ cd ${ps.worktree}
18009
+ \`\`\`
18010
+
18011
+ 2. Fetch and rebase manually:
18012
+ \`\`\`
18013
+ git fetch origin ${ps.baseBranch}
18014
+ git rebase origin/${ps.baseBranch}
18015
+ \`\`\`
18016
+
18017
+ 3. Resolve conflicts in your editor, then:
18018
+ \`\`\`
18019
+ git add .
18020
+ git rebase --continue
18021
+ \`\`\`
18022
+
18023
+ 4. Force push the resolved branch:
18024
+ \`\`\`
18025
+ git push --force-with-lease
18026
+ \`\`\`
18027
+
18028
+ 5. Unblock the plan:
18029
+ \`\`\`
18030
+ prloom unpause ${plan.frontmatter.id}
18031
+ \`\`\`
18032
+
18033
+ The plan is now **blocked** until conflicts are resolved.`);
17871
18034
  } else if (rebaseResult.success) {
17872
18035
  await forcePush(ps.worktree, ps.branch);
17873
18036
  console.log(` Rebased and force-pushed`);
@@ -18241,8 +18404,8 @@ async function runClean(repoRoot) {
18241
18404
  console.log("");
18242
18405
  console.log(`Found ${planIds.length} plan(s) in inbox.`);
18243
18406
  console.log("");
18244
- const readline = await import("readline");
18245
- const rl = readline.createInterface({
18407
+ const readline2 = await import("readline");
18408
+ const rl = readline2.createInterface({
18246
18409
  input: process.stdin,
18247
18410
  output: process.stdout
18248
18411
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prloom",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "prloom": "./dist/cli/index.js"
@@ -2,11 +2,10 @@
2
2
 
3
3
  You are processing PR feedback for an active plan. Your job is to:
4
4
 
5
- 1. Analyze all review feedback (comments, reviews, inline comments)
6
- 2. Create specific, actionable TODO items for the plan
7
- 3. Group related feedback into fewer tasks where appropriate
5
+ 1. **Classify** each feedback item by type
6
+ 2. **Respond** appropriately based on the type
7
+ 3. **Update** the plan if changes are needed
8
8
  4. Detect if a rebase was requested
9
- 5. Compose a reply message for the reviewer(s)
10
9
 
11
10
  ## Feedback to Process
12
11
 
@@ -16,9 +15,36 @@ You are processing PR feedback for an active plan. Your job is to:
16
15
 
17
16
  {{plan}}
18
17
 
19
- ## Instructions
18
+ ---
20
19
 
21
- ### Adding TODOs
20
+ ## Step 1: Classify Each Feedback Item
21
+
22
+ Before acting, identify the type of each feedback item:
23
+
24
+ | Type | Description | Action |
25
+ | ------------------- | --------------------------------------------- | ------------------------ |
26
+ | **Question** | Asking "why", "how", or seeking clarification | Answer in reply, NO TODO |
27
+ | **Change Request** | Asking for code modifications | Create specific TODO |
28
+ | **Approval/Praise** | Positive feedback, LGTM, approval | Acknowledge briefly |
29
+ | **Process Request** | Rebase, update branch, etc. | Set flag, acknowledge |
30
+
31
+ ## Step 2: Handle Each Type
32
+
33
+ ### Questions → Answer Directly
34
+
35
+ For questions, **answer them substantively in your reply**:
36
+
37
+ - Explore the codebase to find the answer if needed
38
+ - Explain the reasoning behind implementation decisions
39
+ - Reference specific code locations or commits if helpful
40
+ - Do NOT create a TODO like "answer the question" - just answer it
41
+
42
+ Example question: "Why did you use a polling approach instead of webhooks?"
43
+ → Reply with the actual reasoning, don't create a TODO.
44
+
45
+ ### Change Requests → Create TODOs
46
+
47
+ For explicit requests to modify code:
22
48
 
23
49
  - Edit the plan file directly to add actionable TODOs
24
50
  - Add them at the end of the ## TODO section
@@ -28,13 +54,23 @@ You are processing PR feedback for an active plan. Your job is to:
28
54
  - "Update function X to handle null input"
29
55
  - "Add test for Y edge case"
30
56
  - "Rename Z for clarity"
31
- - "Remove deprecated code in file A"
32
- - Group related comments into single tasks when logical
57
+ - Group related requests into single tasks when logical
33
58
  - If the plan status is `done` and you add TODOs, change status to `active`
34
59
 
35
- ### Writing the Result File
60
+ ### Approval/Praise Acknowledge
61
+
62
+ Simply thank the reviewer in your reply. No TODO needed.
63
+
64
+ ### Process Requests → Set Flag
65
+
66
+ If any comment mentions rebase, update branch, or similar:
67
+
68
+ - Set `rebase_requested: true` in the result file
69
+ - Acknowledge in your reply
70
+
71
+ ## Step 3: Write the Result File
36
72
 
37
- After editing the plan, you MUST write `.prloom/triage-result.json` with:
73
+ After processing, you MUST write `prloom/.local/triage-result.json`:
38
74
 
39
75
  ```json
40
76
  {
@@ -45,19 +81,20 @@ After editing the plan, you MUST write `.prloom/triage-result.json` with:
45
81
 
46
82
  **Required fields:**
47
83
 
48
- - `reply_markdown`: Your response to post on the PR (ALWAYS required, even for no-ops)
49
- - `rebase_requested`: Set to `true` if any comment mentions rebase, update branch, or similar
84
+ - `reply_markdown`: Your response to post on the PR (ALWAYS required)
85
+ - `rebase_requested`: Set to `true` if rebase was requested
50
86
 
51
87
  ### Reply Guidelines
52
88
 
53
89
  - Be polite and professional
54
- - Acknowledge specific feedback points
55
- - Explain what TODOs were created
56
- - If no changes needed, explain why
57
- - Keep it concise
90
+ - **Answer questions directly** - this is the most important part
91
+ - Explain what TODOs were created (if any)
92
+ - If a request doesn't require changes, explain why
93
+ - Keep it concise but complete
58
94
 
59
95
  ## Critical Rules
60
96
 
61
- 1. You MUST write `.prloom/triage-result.json` even if you add no TODOs
97
+ 1. You MUST write `prloom/.local/triage-result.json` even if you add no TODOs
62
98
  2. The result file must contain valid JSON only, no markdown wrapper
63
99
  3. Failure to write the result file will mark the plan as blocked
100
+ 4. **Questions should be answered, not converted to TODOs**