@yemi33/minions 0.1.2222 → 0.1.2224
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/dashboard.js +81 -6
- package/docs/branch-derivation.md +34 -0
- package/engine/ado.js +20 -0
- package/engine/github.js +18 -0
- package/engine/pr-action.js +10 -3
- package/engine/shared.js +33 -0
- package/package.json +1 -1
- package/prompts/cc-system.md +1 -1
package/dashboard.js
CHANGED
|
@@ -512,7 +512,64 @@ function inferActionPrRecord(action, prs, project = null) {
|
|
|
512
512
|
return findPrRecordReferencedByText(prs, text, project);
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
-
|
|
515
|
+
// issue #246 — verify a LOOSE (description/title-scanned) PR ref points at a
|
|
516
|
+
// real pull request before `copyWorkItemPrFields` promotes it to structured
|
|
517
|
+
// targetPr/pr_id/prNumber fields. GitHub & ADO issues share the PR number
|
|
518
|
+
// namespace, so a bare `owner/repo#244` / `#244` can be an ISSUE. Stamping an
|
|
519
|
+
// issue mints a phantom pull-requests.json record (agent 'human', goes
|
|
520
|
+
// abandoned) AND routes the dispatch through the PR-fix path instead of
|
|
521
|
+
// implement-and-open-a-fresh-PR.
|
|
522
|
+
//
|
|
523
|
+
// Resolution order (cheap → expensive, fail-safe):
|
|
524
|
+
// 1. An already-tracked PR record for the ref ⇒ it's a known PR ⇒ true (no net).
|
|
525
|
+
// 2. Host existence check (gh/ado prExists) ⇒ true | false | null.
|
|
526
|
+
// Returns true only when a PR is positively confirmed; false / null / unknown
|
|
527
|
+
// all mean "do not stamp" (prefer a fresh PR over a phantom one).
|
|
528
|
+
const _PR_REF_VERIFY_TTL_MS = 5 * 60 * 1000;
|
|
529
|
+
const _prRefVerifyCache = new Map(); // cacheKey → { value: boolean, at: ms }
|
|
530
|
+
let _prRefVerifierOverride = null; // test seam
|
|
531
|
+
|
|
532
|
+
// Test seam (issue #246) — inject a fake verifier so handler tests can assert
|
|
533
|
+
// the stamp decision without a live gh/ado call. The override receives
|
|
534
|
+
// (prRef, project) and returns true | false | null.
|
|
535
|
+
function _setPrRefVerifierForTest(fn) { _prRefVerifierOverride = (typeof fn === 'function') ? fn : null; }
|
|
536
|
+
|
|
537
|
+
function _findTrackedPrRecord(prRef, project) {
|
|
538
|
+
if (!project) return null;
|
|
539
|
+
try {
|
|
540
|
+
const prs = shared.safeJsonArr(shared.projectPrPath(project));
|
|
541
|
+
return shared.findPrRecord(prs, prRef, project);
|
|
542
|
+
} catch { return null; }
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function verifyLoosePrRefIsPr(prRef, project) {
|
|
546
|
+
if (_prRefVerifierOverride) {
|
|
547
|
+
try { return (await _prRefVerifierOverride(prRef, project)) === true; }
|
|
548
|
+
catch { return false; }
|
|
549
|
+
}
|
|
550
|
+
// 1. Known PR record ⇒ definitely a PR (no network call needed).
|
|
551
|
+
if (_findTrackedPrRecord(prRef, project)) return true;
|
|
552
|
+
// 2. Host existence check.
|
|
553
|
+
const info = shared.classifyPrRefForVerification(prRef, project);
|
|
554
|
+
if (!info) return false; // can't resolve host/number → fail-safe, do not stamp
|
|
555
|
+
const cacheKey = info.host === 'github'
|
|
556
|
+
? `github:${info.slug}#${info.prNumber}`
|
|
557
|
+
: `ado:${info.adoOrg}/${info.adoProject}/${info.adoRepo}#${info.prNumber}`;
|
|
558
|
+
const cached = _prRefVerifyCache.get(cacheKey);
|
|
559
|
+
if (cached && (Date.now() - cached.at) < _PR_REF_VERIFY_TTL_MS) return cached.value;
|
|
560
|
+
let verdict = null;
|
|
561
|
+
try {
|
|
562
|
+
verdict = info.host === 'github'
|
|
563
|
+
? await gh.prExists(info.slug, info.prNumber)
|
|
564
|
+
: await ado.prExists(info.adoOrg, info.adoProject, info.adoRepo, info.prNumber);
|
|
565
|
+
} catch { verdict = null; }
|
|
566
|
+
const value = verdict === true;
|
|
567
|
+
// Only cache a definitive answer; an unknown (null) may resolve next time.
|
|
568
|
+
if (verdict === true || verdict === false) _prRefVerifyCache.set(cacheKey, { value, at: Date.now() });
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function copyWorkItemPrFields(item, input, pr = null, opts = {}) {
|
|
516
573
|
// W-mqbaby2a000pa8ee: Gate the LOOSE description/title scan in
|
|
517
574
|
// `shared.extractWorkItemPrRef` on `type: "fix"`. Without this gate,
|
|
518
575
|
// an implement/explore/test WI that merely mentions an existing PR
|
|
@@ -526,13 +583,30 @@ function copyWorkItemPrFields(item, input, pr = null) {
|
|
|
526
583
|
// operator intent — they stamp on EVERY type. An explicit `pr` record
|
|
527
584
|
// (caller already resolved the PR) also bypasses the gate. Only the
|
|
528
585
|
// last-resort description/title regex scan is type-gated.
|
|
529
|
-
|
|
586
|
+
//
|
|
587
|
+
// issue #246: a loose `github:owner/repo#NNN` / `#NNN` token can be an
|
|
588
|
+
// ISSUE — issues and PRs share one number namespace. Before stamping a
|
|
589
|
+
// loose ref we verify it points at a real PR (already-tracked record or a
|
|
590
|
+
// gh/ado existence check); structured refs are trusted without a network
|
|
591
|
+
// call. Fail-safe: if existence can't be confirmed, do NOT stamp — the WI
|
|
592
|
+
// then dispatches as a normal fix that opens a fresh PR.
|
|
593
|
+
let prRef;
|
|
594
|
+
if (pr) {
|
|
595
|
+
prRef = getWorkItemPrRef(input);
|
|
596
|
+
} else {
|
|
530
597
|
const structuredRef = shared.extractStructuredWorkItemPrRef(input);
|
|
531
|
-
if (
|
|
532
|
-
|
|
598
|
+
if (structuredRef) {
|
|
599
|
+
prRef = structuredRef; // explicit operator intent — trusted, no verification
|
|
600
|
+
} else {
|
|
601
|
+
if (String(item?.type || '').toLowerCase() !== WORK_TYPE.FIX) return;
|
|
602
|
+
const looseRef = getWorkItemPrRef(input);
|
|
603
|
+
if (!looseRef) return;
|
|
604
|
+
// issue #246 — confirm the loose ref is a PR (not an issue) before stamping.
|
|
605
|
+
const confirmed = await verifyLoosePrRefIsPr(looseRef, opts.project || null);
|
|
606
|
+
if (!confirmed) return; // issue / unverifiable → dispatch as a fresh fix
|
|
607
|
+
prRef = looseRef;
|
|
533
608
|
}
|
|
534
609
|
}
|
|
535
|
-
const prRef = getWorkItemPrRef(input);
|
|
536
610
|
if (!prRef && !pr) return;
|
|
537
611
|
const prNumber = pr ? shared.getPrNumber(pr) : shared.getPrNumber(prRef);
|
|
538
612
|
item.targetPr = pr?.id || prRef;
|
|
@@ -6361,7 +6435,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
6361
6435
|
const originWi = extractMinionsOriginWiHeader(req);
|
|
6362
6436
|
if (originAgent) item._originAgent = originAgent;
|
|
6363
6437
|
if (originWi) item._originWi = originWi;
|
|
6364
|
-
copyWorkItemPrFields(item, body);
|
|
6438
|
+
await copyWorkItemPrFields(item, body, null, { project: targetProject });
|
|
6365
6439
|
// W-mq5wfh1v000e0da9 — Auto-enroll the PR into pull-requests.json when
|
|
6366
6440
|
// this is a `type: fix` WI carrying a structured PR pointer and the PR
|
|
6367
6441
|
// isn't tracked yet. Without this, the engine's `pr_not_found` gate
|
|
@@ -13857,6 +13931,7 @@ function _installCrashHandlers() {
|
|
|
13857
13931
|
// Production entry points use the closures directly; tests import via require('./dashboard').
|
|
13858
13932
|
module.exports = {
|
|
13859
13933
|
getMcpServers,
|
|
13934
|
+
_setPrRefVerifierForTest, // issue #246 — inject a fake loose-PR-ref verifier in handler tests
|
|
13860
13935
|
_parseClaudeMcpListLine,
|
|
13861
13936
|
_parseCopilotMcpListJson,
|
|
13862
13937
|
_readWorkspaceMcpServers,
|
|
@@ -56,6 +56,40 @@ PR-fix dispatch path, fail the PR-branch lookup, and stick in
|
|
|
56
56
|
(`targetPr` / `prUrl` / `references[].url` / etc.) still stamps on
|
|
57
57
|
every type — only the loose regex scan is gated.
|
|
58
58
|
|
|
59
|
+
## Loose refs are PR-verified before stamping (issue #246)
|
|
60
|
+
|
|
61
|
+
A loose `github:owner/repo#NNN` / `ado:org/proj/repo#NNN` / bare `#NNN` token
|
|
62
|
+
carries **no issue-vs-PR discriminator** — GitHub issues and PRs share one
|
|
63
|
+
number namespace, and ADO work items share theirs. Before this guard, a
|
|
64
|
+
`type: "fix"` WI titled `Fix github:owner/repo#244: …` where **#244 is an
|
|
65
|
+
issue** got `targetPr` / `pr_id` / `prNumber` stamped by
|
|
66
|
+
`dashboard.js#copyWorkItemPrFields`, which then (a) minted a phantom
|
|
67
|
+
`pull-requests.json` record (`agent: 'human'`, went abandoned, polluted the PR
|
|
68
|
+
count) via `shared.autoEnrollPrFromFixWorkItem`, and (b) routed the dispatch
|
|
69
|
+
through the PR-fix path (`targetPr`/`pr_id`/`prNumber`) instead of
|
|
70
|
+
implement-and-open-a-fresh-PR.
|
|
71
|
+
|
|
72
|
+
`copyWorkItemPrFields` now verifies a **loose** ref points at a real PR before
|
|
73
|
+
stamping:
|
|
74
|
+
|
|
75
|
+
1. **Structured refs are trusted** — `targetPr` / `prUrl` / `references[].url`
|
|
76
|
+
/ `meta.pr_followup.parent_pr_url` are explicit operator intent and stamp
|
|
77
|
+
with **no** network call.
|
|
78
|
+
2. **Loose (description/title-scanned) refs on a fix WI** are confirmed via:
|
|
79
|
+
- an already-tracked `pull-requests.json` record for the ref (cheap, no
|
|
80
|
+
network), else
|
|
81
|
+
- a host existence check — `engine/github.js#prExists` (`gh api
|
|
82
|
+
repos/:owner/:repo/pulls/NNN`; 404 ⇒ issue) or `engine/ado.js#prExists`
|
|
83
|
+
(`…/pullrequests/NNN`), routed via `engine/gh-token.js` per-account token.
|
|
84
|
+
`shared.classifyPrRefForVerification(prRef, project)` does the pure host/slug
|
|
85
|
+
/number routing; the result is TTL-cached.
|
|
86
|
+
3. **Fail-safe:** when existence can't be confirmed (issue, 404, network/auth
|
|
87
|
+
error, unresolvable host) the ref is **not** stamped — the WI dispatches as a
|
|
88
|
+
normal fix that opens a NEW PR. Prefer a fresh PR over a phantom one.
|
|
89
|
+
|
|
90
|
+
The `type: "fix"` gate from W-mqbaby2a000pa8ee still applies first: non-fix WIs
|
|
91
|
+
are never stamped from loose prose.
|
|
92
|
+
|
|
59
93
|
## Structured-vs-loose split (W-mq18ec6h000p7b87)
|
|
60
94
|
|
|
61
95
|
The PR-ref extractor has **two** variants — pick the right one for the
|
package/engine/ado.js
CHANGED
|
@@ -2511,6 +2511,25 @@ async function fetchAdoPrMetadata(prNum, adoOrg, adoProj, adoRepo) {
|
|
|
2511
2511
|
};
|
|
2512
2512
|
}
|
|
2513
2513
|
|
|
2514
|
+
/**
|
|
2515
|
+
* issue #246 — confirm a number points at a real ADO PULL REQUEST. ADO work
|
|
2516
|
+
* items (issues) live in a different namespace, so a non-PR number 404s on the
|
|
2517
|
+
* pullrequests endpoint. `adoFetch` can't distinguish 404 from auth/network
|
|
2518
|
+
* failure, so a missing/erroring PR returns null (not false) — and the caller
|
|
2519
|
+
* treats both false and null as "do not stamp" (fail-safe). Returns true only
|
|
2520
|
+
* on a positively-fetched PR.
|
|
2521
|
+
*/
|
|
2522
|
+
async function prExists(adoOrg, adoProject, adoRepo, prNumber) {
|
|
2523
|
+
const n = parseInt(prNumber, 10);
|
|
2524
|
+
if (!adoOrg || !adoProject || !adoRepo || !Number.isInteger(n) || n <= 0) return null;
|
|
2525
|
+
try {
|
|
2526
|
+
const meta = await fetchAdoPrMetadata(n, adoOrg, adoProject, adoRepo);
|
|
2527
|
+
return meta ? true : null;
|
|
2528
|
+
} catch {
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2514
2533
|
/**
|
|
2515
2534
|
* Fetch live PR and build status for a single PR number.
|
|
2516
2535
|
* Used by engine/ado-status.js so agents can check CI without raw curl calls.
|
|
@@ -2918,6 +2937,7 @@ module.exports = {
|
|
|
2918
2937
|
getAdoThrottleState,
|
|
2919
2938
|
getAdoThrottleStateAll,
|
|
2920
2939
|
fetchAdoPrMetadata,
|
|
2940
|
+
prExists, // issue #246 — confirm a loose ref points at a PR (not a work item) before stamping
|
|
2921
2941
|
fetchSinglePrBuildStatus,
|
|
2922
2942
|
findOpenPrOnBranch,
|
|
2923
2943
|
applyAdoPrMetadata, // #3079 — exported for unit tests of targetRefName + retarget reset
|
package/engine/github.js
CHANGED
|
@@ -400,6 +400,23 @@ async function ghApi(endpoint, slug, opts = {}) {
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
+
/**
|
|
404
|
+
* issue #246 — confirm a number points at a real PULL REQUEST (GitHub issues
|
|
405
|
+
* and PRs share one number namespace, so `owner/repo#244` may be an issue).
|
|
406
|
+
* Hits GET /repos/{slug}/pulls/{n}, which 404s for issue numbers. Returns:
|
|
407
|
+
* true — the PR exists
|
|
408
|
+
* false — 404 (the number is an issue or doesn't exist)
|
|
409
|
+
* null — could not determine (network / auth / throttle) → caller fail-safe
|
|
410
|
+
*/
|
|
411
|
+
async function prExists(slug, prNumber, opts = {}) {
|
|
412
|
+
const n = parseInt(prNumber, 10);
|
|
413
|
+
if (!slug || !Number.isInteger(n) || n <= 0) return null;
|
|
414
|
+
const result = await ghApi(`/pulls/${n}`, slug, opts);
|
|
415
|
+
if (result === GH_NOT_FOUND) return false;
|
|
416
|
+
if (result && typeof result === 'object' && result._notFound !== true) return true;
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
|
|
403
420
|
const BUILD_ERROR_LOG_MAX_LINES = 150;
|
|
404
421
|
|
|
405
422
|
/**
|
|
@@ -1885,6 +1902,7 @@ module.exports = {
|
|
|
1885
1902
|
isGhThrottled,
|
|
1886
1903
|
getGhThrottleState,
|
|
1887
1904
|
ghApi, // P-8c1b6e45 — used by engine/shared-branch-pr-reconcile.js to list open PRs on a feature branch
|
|
1905
|
+
prExists, // issue #246 — confirm a loose ref points at a PR (not an issue) before stamping
|
|
1888
1906
|
// Exported for testing
|
|
1889
1907
|
isGitHub,
|
|
1890
1908
|
getRepoSlug,
|
package/engine/pr-action.js
CHANGED
|
@@ -155,7 +155,7 @@ function buildCreatePrFollowups({ project, branch, contextOnly = false } = {}) {
|
|
|
155
155
|
if (!proj) return [];
|
|
156
156
|
const br = branch && String(branch).trim() ? String(branch).trim() : '';
|
|
157
157
|
const branchClause = br
|
|
158
|
-
? `on the
|
|
158
|
+
? `(the working tree is on branch ${br}; if ${br} is the project's default/main branch, create a new well-named branch off it and commit there rather than committing to ${br} directly)`
|
|
159
159
|
: 'on a new well-named branch off the project main branch';
|
|
160
160
|
const linkBody = contextOnly
|
|
161
161
|
? `{"url":"<new PR url>","project":"${proj}","contextOnly":true}`
|
|
@@ -163,11 +163,18 @@ function buildCreatePrFollowups({ project, branch, contextOnly = false } = {}) {
|
|
|
163
163
|
const trackNote = contextOnly
|
|
164
164
|
? 'so it is tracked but not auto-reviewed'
|
|
165
165
|
: 'so the engine auto-manages it (review -> fix -> re-review -> auto-merge)';
|
|
166
|
+
// The original branch CC must return to once the PR is open, so the new PR
|
|
167
|
+
// branch is never left checked out in the shared operator working tree.
|
|
168
|
+
const restoreClause = br
|
|
169
|
+
? `switch the working tree back to the ORIGINAL branch (${br})`
|
|
170
|
+
: 'switch the working tree back to the original branch it started on';
|
|
166
171
|
const message =
|
|
167
172
|
`Create a PR from the local changes you just made in the ${proj} project. ` +
|
|
168
|
-
|
|
173
|
+
(br ? `Remember the working tree's current branch (${br}) as the ORIGINAL branch to return to when done. ` : '') +
|
|
174
|
+
`Steps: (1) in that project's working tree, stage and commit the modified files with a clear conventional-commit message ${branchClause}; ` +
|
|
169
175
|
`(2) push the branch; (3) open a PR against the project's main branch using the right CLI for the repo host (gh for GitHub, az repos for ADO); ` +
|
|
170
|
-
`(4) link it to the tracker by calling POST /api/pull-requests/link with ${linkBody} ${trackNote}
|
|
176
|
+
`(4) link it to the tracker by calling POST /api/pull-requests/link with ${linkBody} ${trackNote}; ` +
|
|
177
|
+
`(5) finally, ${restoreClause} — do not leave the new PR branch checked out. ` +
|
|
171
178
|
`Then show me the PR URL.`;
|
|
172
179
|
return [{ kind: CREATE_PR_FOLLOWUP.kind, label: CREATE_PR_FOLLOWUP.label, project: proj, branch: br || null, message }];
|
|
173
180
|
}
|
package/engine/shared.js
CHANGED
|
@@ -7053,6 +7053,38 @@ function deriveUrlForPrRef(prRef, project) {
|
|
|
7053
7053
|
return null;
|
|
7054
7054
|
}
|
|
7055
7055
|
|
|
7056
|
+
// issue #246 — classify a PR ref into the host + identifiers needed to verify
|
|
7057
|
+
// it actually points at a PULL REQUEST (not an ISSUE, which shares the number
|
|
7058
|
+
// namespace on GitHub and is a distinct endpoint on ADO). Pure: no network.
|
|
7059
|
+
//
|
|
7060
|
+
// Resolution: prefer the scope embedded in the ref (canonical `github:…#N` /
|
|
7061
|
+
// `ado:…#N` id, or a PR URL); fall back to the project's own scope for a bare
|
|
7062
|
+
// `#N` / `PR-N` / number ref. Returns null when the ref can't be resolved to a
|
|
7063
|
+
// concrete host + number — the caller treats null as "cannot verify" and, per
|
|
7064
|
+
// the fail-safe contract, declines to stamp.
|
|
7065
|
+
//
|
|
7066
|
+
// Returns one of:
|
|
7067
|
+
// { host: 'github', slug: 'owner/repo', prNumber }
|
|
7068
|
+
// { host: 'ado', adoOrg, adoProject, adoRepo, prNumber }
|
|
7069
|
+
// null
|
|
7070
|
+
function classifyPrRefForVerification(prRef, project = null) {
|
|
7071
|
+
const scoped = getPrScopeInfo(prRef);
|
|
7072
|
+
let scope = scoped?.scope || '';
|
|
7073
|
+
const prNumber = scoped?.prNumber ?? getPrNumber(prRef);
|
|
7074
|
+
if (!scope && project) scope = getProjectPrScope(project);
|
|
7075
|
+
if (!scope || prNumber == null) return null;
|
|
7076
|
+
if (scope.startsWith('github:')) {
|
|
7077
|
+
const slug = scope.slice('github:'.length);
|
|
7078
|
+
return slug.includes('/') ? { host: 'github', slug, prNumber } : null;
|
|
7079
|
+
}
|
|
7080
|
+
if (scope.startsWith('ado:')) {
|
|
7081
|
+
const parts = scope.slice('ado:'.length).split('/');
|
|
7082
|
+
if (parts.length < 3 || !parts[0] || !parts[1] || !parts[2]) return null;
|
|
7083
|
+
return { host: 'ado', adoOrg: parts[0], adoProject: parts[1], adoRepo: parts[2], prNumber };
|
|
7084
|
+
}
|
|
7085
|
+
return null;
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7056
7088
|
// W-mq5wfh1v000e0da9 — Auto-enroll a PR into pull-requests.json when a
|
|
7057
7089
|
// `type: fix` work item is created with a structured PR pointer. Without
|
|
7058
7090
|
// this, fix WIs against untracked PRs bypass the polling / review / build
|
|
@@ -8775,6 +8807,7 @@ module.exports = {
|
|
|
8775
8807
|
migratePrGateFlags, // W-mq5s5ttx000j7ab8-a — boot migration wired from engine/cli.js
|
|
8776
8808
|
autoEnrollPrFromFixWorkItem,
|
|
8777
8809
|
deriveUrlForPrRef, // exported for testing
|
|
8810
|
+
classifyPrRefForVerification, // issue #246 — host routing for loose PR-ref verification
|
|
8778
8811
|
nextWorkItemId,
|
|
8779
8812
|
getProjectOrg,
|
|
8780
8813
|
getAdoOrgBase,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2224",
|
|
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"
|
package/prompts/cc-system.md
CHANGED
|
@@ -96,7 +96,7 @@ curl -s -X POST http://localhost:{{dashboard_port}}/api/pr-action/offer-create-p
|
|
|
96
96
|
-H 'Content-Type: application/json' -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
|
|
97
97
|
-d '{"project":"<project name>"}'
|
|
98
98
|
```
|
|
99
|
-
This checks the project's working tree (`git status --porcelain`) and, when there are uncommitted changes, returns a **`[Create PR]`** follow-up chip to the user (same chip mechanism as the `pr-action` `[Comment]`/`[Fix once]`/`[Track for auto-fix]` chips). When the user clicks it, you'll receive a turn instructing you to commit → push → open the PR → link it to the tracker. Pass `"contextOnly":true` if the PR should be tracked-but-not-auto-reviewed; omit it to have the engine auto-manage the PR (review → fix → re-review → auto-merge). If `hasChanges` is `false`, there's nothing to PR — skip the offer. Don't commit/push on your own initiative; surface the chip and let the user decide.
|
|
99
|
+
This checks the project's working tree (`git status --porcelain`) and, when there are uncommitted changes, returns a **`[Create PR]`** follow-up chip to the user (same chip mechanism as the `pr-action` `[Comment]`/`[Fix once]`/`[Track for auto-fix]` chips). When the user clicks it, you'll receive a turn instructing you to commit → push → open the PR → link it to the tracker → switch the working tree back to the original branch (never leave the new PR branch checked out in the shared operator tree). Pass `"contextOnly":true` if the PR should be tracked-but-not-auto-reviewed; omit it to have the engine auto-manage the PR (review → fix → re-review → auto-merge). If `hasChanges` is `false`, there's nothing to PR — skip the offer. Don't commit/push on your own initiative; surface the chip and let the user decide.
|
|
100
100
|
|
|
101
101
|
## When to dispatch vs answer inline
|
|
102
102
|
|