@yemi33/minions 0.1.2196 → 0.1.2198

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/engine/shared.js CHANGED
@@ -6348,6 +6348,15 @@ function applyPrFieldDelta(target, before, after) {
6348
6348
  function normalizePrRecord(pr, project = null) {
6349
6349
  if (!pr || typeof pr !== 'object') return false;
6350
6350
  let changed = false;
6351
+ const hasCanonicalContextOnly = Object.prototype.hasOwnProperty.call(pr, 'contextOnly');
6352
+ if (hasCanonicalContextOnly) {
6353
+ for (const legacyKey of ['_contextOnly', '_autoObserve', '_manual']) {
6354
+ if (Object.prototype.hasOwnProperty.call(pr, legacyKey)) {
6355
+ delete pr[legacyKey];
6356
+ changed = true;
6357
+ }
6358
+ }
6359
+ }
6351
6360
  const prNumber = getPrNumber(pr.prNumber ?? pr.id ?? pr.url);
6352
6361
  if (prNumber != null && pr.prNumber !== prNumber) {
6353
6362
  pr.prNumber = prNumber;
@@ -6386,17 +6395,19 @@ function normalizePrLinkItems(value) {
6386
6395
  return [...new Set(items.filter(item => typeof item === 'string' && item))];
6387
6396
  }
6388
6397
 
6389
- // W-mq5s5ttx000j7ab8-a — canonical `contextOnly` gate. The 6-clause legacy
6390
- // body (prdItems / _autoObserve / sourcePlan / itemType / agent-heuristic) is
6391
- // replaced by a single read of the canonical field. The one-shot boot
6392
- // migration (migratePrGateFlags) projects the legacy signals onto
6393
- // `contextOnly` so every on-disk record has the canonical value before any
6394
- // tick fires. Anything new written via upsertPullRequestRecord (and the
6395
- // dashboard link/observe paths consolidated in PR #3137 / `-c`) also lands
6396
- // on `contextOnly` directly. See W-mq5rs2eq000da8a9 for the bug repro that
6397
- // motivated consolidating onto a single helper.
6398
+ // W-mq5s5ttx000j7ab8-a / W-mqerisvz000n3901 — canonical `contextOnly` gate
6399
+ // with a legacy bridge. Canonical `contextOnly` wins whenever it exists; legacy
6400
+ // `_contextOnly` is only a fallback for pre-migration/raw records. This keeps
6401
+ // observe toggles (`contextOnly:false`) from being overridden by stale legacy
6402
+ // contamination while still treating old context-only rows as reference-only.
6403
+ function isContextOnlyPrRecord(pr) {
6404
+ if (!pr || typeof pr !== 'object') return false;
6405
+ if (typeof pr.contextOnly === 'boolean') return pr.contextOnly;
6406
+ return pr._contextOnly === true;
6407
+ }
6408
+
6398
6409
  function isAutoManagedPrRecord(pr) {
6399
- return !!pr && typeof pr === 'object' && pr.contextOnly !== true;
6410
+ return !!pr && typeof pr === 'object' && !isContextOnlyPrRecord(pr);
6400
6411
  }
6401
6412
 
6402
6413
  function mergePrLinkItems(links, prId, itemIds) {
@@ -6548,6 +6559,13 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
6548
6559
  prNumber: prNumber ?? entry.prNumber ?? null,
6549
6560
  prdItems: linkedItemIds,
6550
6561
  };
6562
+ const normalizedEntryContextOnly = normalizedEntry.contextOnly != null
6563
+ ? normalizedEntry.contextOnly
6564
+ : normalizedEntry._contextOnly;
6565
+ if (normalizedEntryContextOnly != null) {
6566
+ normalizedEntry.contextOnly = normalizedEntryContextOnly === true;
6567
+ delete normalizedEntry._contextOnly;
6568
+ }
6551
6569
 
6552
6570
  let created = false;
6553
6571
  let linked = false;
@@ -6568,7 +6586,8 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
6568
6586
  } else {
6569
6587
  target.id = canonicalId;
6570
6588
  if (prNumber != null) target.prNumber = prNumber;
6571
- const targetWasAutoManaged = isAutoManagedPrRecord(target);
6589
+ const targetWasAutoManaged = isAutoManagedPrRecord(target)
6590
+ || (target.contextOnly == null && target._contextOnly === true && _prRecordIsLegacyManaged(target));
6572
6591
  for (const key of ['url', 'title', 'description', 'agent', 'branch', 'reviewStatus', 'status', 'created', 'sourcePlan', 'itemType']) {
6573
6592
  if (normalizedEntry[key] != null && normalizedEntry[key] !== '' && (target[key] == null || target[key] === '')) {
6574
6593
  target[key] = normalizedEntry[key];
@@ -6591,7 +6610,10 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
6591
6610
  : normalizedEntry._contextOnly;
6592
6611
  if (incomingContextOnly != null) {
6593
6612
  const wouldDemoteManagedPr = incomingContextOnly === true && targetWasAutoManaged;
6594
- if (!wouldDemoteManagedPr) target.contextOnly = incomingContextOnly === true;
6613
+ target.contextOnly = wouldDemoteManagedPr ? false : incomingContextOnly === true;
6614
+ delete target._contextOnly;
6615
+ delete target._autoObserve;
6616
+ delete target._manual;
6595
6617
  }
6596
6618
  }
6597
6619
  target.prdItems = normalizePrLinkItems(target.prdItems || []);
@@ -8298,7 +8320,7 @@ module.exports = {
8298
8320
  normalizePrRecords,
8299
8321
  normalizePrLinkItems, // exported for testing
8300
8322
  mergePrLinkItems, // exported for testing
8301
- isAutoManagedPrRecord,
8323
+ isContextOnlyPrRecord,
8302
8324
  upsertPullRequestRecord,
8303
8325
  isAutoManagedPrRecord, // W-mq5s5ttx000j7ab8-a — exported for engine + watch-plugin gate consolidation
8304
8326
  migratePrGateFlags, // W-mq5s5ttx000j7ab8-a — boot migration wired from engine/cli.js
package/engine.js CHANGED
@@ -6043,7 +6043,7 @@ async function discoverFromPrs(config, project) {
6043
6043
 
6044
6044
  for (const pr of prs) {
6045
6045
  if (pr.status !== PR_STATUS.ACTIVE) continue;
6046
- if (pr._contextOnly) {
6046
+ if (shared.isContextOnlyPrRecord(pr)) {
6047
6047
  _logPrDispatchSkipOnce(pr, 'context-only');
6048
6048
  continue;
6049
6049
  }
@@ -6057,11 +6057,9 @@ async function discoverFromPrs(config, project) {
6057
6057
  log('info', `Branch mutex: skipping PR ${pr.id} dispatch — branch ${prBranchForMutex} locked by another agent`);
6058
6058
  continue;
6059
6059
  }
6060
- // Auto-managed gate: single source of truth in engine/shared.js.
6061
- // Honors prdItems, _autoObserve, sourcePlan/itemType, and any non-empty
6062
- // non-'human' agent author. See W-mq5rs2eq000da8a9 for the bug fix —
6063
- // the prior inline `knownAgents.has(...) || prdItems || (_manual && !_contextOnly)`
6064
- // silently dropped PRs with `_autoObserve: true` + human author.
6060
+ // Auto-managed gate: single source of truth in engine/shared.js. Canonical
6061
+ // `contextOnly` wins over stale legacy flags; legacy `_contextOnly` is only
6062
+ // a fallback for raw pre-migration records.
6065
6063
  if (!shared.isAutoManagedPrRecord(pr)) {
6066
6064
  _logPrDispatchSkipOnce(pr, 'not-auto-managed');
6067
6065
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2196",
3
+ "version": "0.1.2198",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"
@@ -182,6 +182,39 @@ curl -s http://localhost:{{dashboard_port}}/api/work-items
182
182
  curl -s http://localhost:{{dashboard_port}}/api/status
183
183
  ```
184
184
 
185
+ ## Read-only PR actions on ANY PR — POST /api/pr-action
186
+
187
+ When the user asks you to **review / summarize / comment-on / triage a specific PR by URL or id** — including PRs in repos that are NOT a configured Minions project (e.g. "review this PR https://github.com/some/repo/pull/42", "summarize ado:org/proj/repo#5215493", "triage github:yemi33/minions#2702") — route it through `POST /api/pr-action` instead of dispatching a `review`/`explore` work item. This path is **projectless and read-only**: it resolves the PR, fetches its diff + metadata with NO clone/worktree, and reasons over the (untrusted-fenced) content. Use it for one-off, look-don't-touch asks on an arbitrary PR.
188
+
189
+ - `url` — REQUIRED. A PR URL (`https://github.com/owner/repo/pull/N`, ADO PR URL) or canonical id (`github:owner/repo#N`, `ado:org/proj/repo#N`).
190
+ - `action` — REQUIRED. One of `review`, `summarize`, `comment`, `triage` (read-only only; a code-mutating `fix` is intentionally NOT accepted here).
191
+ - `execute` — set `true` to run the action now and get its `output` back in the response. Omit (or `false`) to only mint an observable handle without running it. **For a CC turn you almost always want `execute:true`** so you can show the result.
192
+
193
+ Run a read-only review now and show the result:
194
+ ```
195
+ curl -s -X POST http://localhost:{{dashboard_port}}/api/pr-action \
196
+ -H 'Content-Type: application/json' \
197
+ -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
198
+ -d '{"url":"https://github.com/some/repo/pull/42","action":"review","execute":true}'
199
+ ```
200
+ The response is `{ ok, status, output, prId, followups: [...] }`. Present `output` to the user. When `status` is `done` the dashboard renders **follow-up action chips** under your reply — `[Comment]` (draft a PR comment), `[Fix once]` (one-off fix — Phase 2), `[Track for auto-fix]` (enroll in the auto-fix loop — Phase 3) — so the user can take the next step with one click; you don't need to render them yourself. A `status` of `flagged` means the PR content tried a prompt-injection (surface that, don't act on it); `failed` carries an `error` to relay.
201
+
202
+ **When NOT to use it:** if the PR is in a configured project and the user wants the normal review/fix loop (re-review on new commits, auto-fix on failing builds), dispatch a `review`/`fix` work item as usual. `pr-action` is the one-shot, projectless, read-only escape hatch.
203
+
204
+ ### Promote a PR to TRACKED auto-fix — POST /api/pr-action/track
205
+
206
+ When the user wants a one-off PR to become **continuously auto-managed** (the `[Track for auto-fix]` chip, or asks to "track this PR for auto-fix" / "keep fixing this PR automatically"), call `POST /api/pr-action/track` with `{ "url": "<pr>" }`. Enrollment is just wiring — it flips the canonical `contextOnly:false` signal so the EXISTING pollers → `discoverFromPrs` → review / fix / re-review → auto-merge pipeline picks the PR up. There is no new fix engine.
207
+
208
+ ```
209
+ curl -s -X POST http://localhost:{{dashboard_port}}/api/pr-action/track \
210
+ -H 'Content-Type: application/json' \
211
+ -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
212
+ -d '{"url":"https://github.com/some/repo/pull/42"}'
213
+ ```
214
+
215
+ - `status:"enrolled"` (with `autoManaged:true`) — done. Tell the user the PR is now tracked; the normal auto-review/auto-fix/auto-merge loop runs on it from here. No further action.
216
+ - `status:"awaiting-target-choice"` — the PR's repo is **not a configured project**, and ongoing auto-fix needs a PERSISTENT checkout. Show the `options` (Clone & keep is `recommendedTarget` for ongoing tracking; temp clone is discarded after one push and can't back tracking) and **do not clone until the user picks**. To proceed with the recommended path, run the Clone & keep target via `POST /api/pr-action/fix` with `{"url":"<pr>","target":"clone-keep"}` to register the project, then call `POST /api/pr-action/track` again to finish enrollment.
217
+
185
218
  ## QA Sessions — natural-language → POST /api/qa/session
186
219
 
187
220
  When the user describes a UI/E2E flow they want validated against a *live, running* app instance — "QA the login flow on PR #1234", "smoke test the homepage", "test the checkout flow on the `develop` branch", "validate the signup journey on my current worktree", etc. — dispatch a QA Session via `POST /api/qa/session` instead of opening an `implement` or `test` work item. The session pipeline boots the dev-up command as a managed-spawn, drafts a runner-native test (Playwright or Maestro) from the natural-language flows, then executes it against the live target and captures the requested artifacts.