@yemi33/minions 0.1.2364 → 0.1.2366
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 +57 -15
- package/docs/README.md +19 -7
- package/docs/command-center.md +2 -2
- package/docs/constants.md +1 -1
- package/docs/documentation-audit-2026-07-09.md +43 -0
- package/docs/engine-restart.md +2 -2
- package/docs/managed-spawn.md +1 -1
- package/docs/onboarding.md +76 -198
- package/docs/preflight.md +4 -3
- package/docs/tutorials/01-install-and-connect.md +87 -0
- package/docs/tutorials/02-first-task.md +73 -0
- package/docs/tutorials/03-plan-driven-feature.md +73 -0
- package/docs/tutorials/04-runtimes-and-harness.md +77 -0
- package/docs/tutorials/05-schedules-and-watches.md +71 -0
- package/docs/tutorials/06-managed-services.md +85 -0
- package/docs/tutorials/07-cross-repo-plan.md +56 -0
- package/docs/tutorials/08-operations-and-recovery.md +76 -0
- package/docs/tutorials/README.md +29 -0
- package/docs/watches.md +6 -6
- package/engine/lifecycle.js +14 -5
- package/engine/queries.js +17 -8
- package/engine/shared.js +104 -60
- package/engine.js +5 -2
- package/package.json +1 -1
package/engine/lifecycle.js
CHANGED
|
@@ -6695,6 +6695,7 @@ function pruneScopeMismatchDuplicatePrs(config) {
|
|
|
6695
6695
|
config = config || getConfig();
|
|
6696
6696
|
const projects = shared.getProjects(config) || [];
|
|
6697
6697
|
if (projects.length === 0) return { pruned: 0, relocated: 0, scanned: 0 };
|
|
6698
|
+
const projectByName = new Map(projects.filter(project => project?.name).map(project => [project.name, project]));
|
|
6698
6699
|
|
|
6699
6700
|
// Map project name -> canonical scope(s) so we can find which project IS
|
|
6700
6701
|
// the correctly-scoped owner for a given mismatch record. Includes
|
|
@@ -6717,14 +6718,19 @@ function pruneScopeMismatchDuplicatePrs(config) {
|
|
|
6717
6718
|
}
|
|
6718
6719
|
}
|
|
6719
6720
|
|
|
6720
|
-
// Index all PRs by
|
|
6721
|
+
// Index all PRs by repository-scoped identity across all projects.
|
|
6721
6722
|
const store = require('./pull-requests-store');
|
|
6722
6723
|
const allPrs = store.readAllPullRequests() || [];
|
|
6723
6724
|
const byId = new Map();
|
|
6724
6725
|
for (const pr of allPrs) {
|
|
6725
6726
|
if (!pr || !pr.id) continue;
|
|
6726
|
-
|
|
6727
|
-
|
|
6727
|
+
const ownerProject = pr._scope && pr._scope !== 'central'
|
|
6728
|
+
? projectByName.get(pr._scope) || null
|
|
6729
|
+
: null;
|
|
6730
|
+
const identity = shared.getPrIdentityKey(pr, ownerProject);
|
|
6731
|
+
if (!identity) continue;
|
|
6732
|
+
if (!byId.has(identity)) byId.set(identity, []);
|
|
6733
|
+
byId.get(identity).push(pr);
|
|
6728
6734
|
}
|
|
6729
6735
|
|
|
6730
6736
|
// Build per-project delete sets keyed by id, so we batch one mutation per
|
|
@@ -6741,6 +6747,9 @@ function pruneScopeMismatchDuplicatePrs(config) {
|
|
|
6741
6747
|
for (const records of byId.values()) {
|
|
6742
6748
|
for (const rec of records) {
|
|
6743
6749
|
scanned++;
|
|
6750
|
+
// A tombstone is the durable record of explicit user intent. Never prune
|
|
6751
|
+
// it in favor of an active sibling, or the sibling becomes visible again.
|
|
6752
|
+
if (rec.userDeleted === true) continue;
|
|
6744
6753
|
if (!rec._invalidProjectScope || rec._invalidProjectScope.reason !== 'pr_scope_mismatch') continue;
|
|
6745
6754
|
const correctScope = rec._invalidProjectScope.prScope;
|
|
6746
6755
|
if (!correctScope) continue;
|
|
@@ -6831,8 +6840,8 @@ function pruneScopeMismatchDuplicatePrs(config) {
|
|
|
6831
6840
|
return { pruned, relocated, scanned };
|
|
6832
6841
|
}
|
|
6833
6842
|
|
|
6834
|
-
// Repair helper: collapse
|
|
6835
|
-
//
|
|
6843
|
+
// Repair helper: collapse duplicate repository-scoped PR identities across all
|
|
6844
|
+
// project-scoped and central tracker data. Called once per reconcile cycle alongside
|
|
6836
6845
|
// pruneScopeMismatchDuplicatePrs so accumulated duplicates are cleaned up
|
|
6837
6846
|
// without requiring a one-off migration.
|
|
6838
6847
|
function collapseAllDuplicatePrRecords(config) {
|
package/engine/queries.js
CHANGED
|
@@ -1001,25 +1001,34 @@ function getPullRequests(config) {
|
|
|
1001
1001
|
// only tombstones one scope's copy would still have its untouched
|
|
1002
1002
|
// cross-scope duplicate survive grouping below and get picked as the
|
|
1003
1003
|
// dedupe "winner", silently resurrecting a PR the user just deleted.
|
|
1004
|
-
const
|
|
1004
|
+
const identityOf = (pr) => {
|
|
1005
|
+
const ownerProject = pr?._scope && pr._scope !== 'central'
|
|
1006
|
+
? projectByName.get(pr._scope) || null
|
|
1007
|
+
: null;
|
|
1008
|
+
return shared.getPrIdentityKey(pr, ownerProject);
|
|
1009
|
+
};
|
|
1010
|
+
const tombstonedIdentities = new Set();
|
|
1005
1011
|
for (const pr of sqlPrs) {
|
|
1006
|
-
|
|
1012
|
+
const identity = identityOf(pr);
|
|
1013
|
+
if (identity && pr.userDeleted === true) tombstonedIdentities.add(identity);
|
|
1007
1014
|
}
|
|
1008
1015
|
|
|
1009
1016
|
const groups = [];
|
|
1010
|
-
const
|
|
1017
|
+
const groupsByIdentity = new Map();
|
|
1011
1018
|
for (const pr of sqlPrs) {
|
|
1012
1019
|
if (!pr || !pr.id) continue;
|
|
1020
|
+
const identity = identityOf(pr);
|
|
1021
|
+
if (!identity) continue;
|
|
1013
1022
|
// Issue #384/#803: skip tombstoned records (user explicitly deleted), and
|
|
1014
|
-
// skip ANY
|
|
1023
|
+
// skip ANY alias of an identity that has a tombstoned copy elsewhere.
|
|
1015
1024
|
if (pr.userDeleted === true) continue;
|
|
1016
|
-
if (
|
|
1017
|
-
if (!
|
|
1025
|
+
if (tombstonedIdentities.has(identity)) continue;
|
|
1026
|
+
if (!groupsByIdentity.has(identity)) {
|
|
1018
1027
|
const g = [];
|
|
1019
|
-
|
|
1028
|
+
groupsByIdentity.set(identity, g);
|
|
1020
1029
|
groups.push(g);
|
|
1021
1030
|
}
|
|
1022
|
-
|
|
1031
|
+
groupsByIdentity.get(identity).push(pr);
|
|
1023
1032
|
}
|
|
1024
1033
|
for (const group of groups) {
|
|
1025
1034
|
const { winner, dropped } = _pickPrDedupeWinner(group, projects);
|
package/engine/shared.js
CHANGED
|
@@ -6327,16 +6327,18 @@ function resolveAgentTempBaseDir(project, engine, minionsDir) {
|
|
|
6327
6327
|
|
|
6328
6328
|
// Seed the agent COPILOT_HOME's mcp-config (copy of `~/.copilot/mcp-config.json`
|
|
6329
6329
|
// MINUS `engine.copilotAgentDisabledMcpServers`) and return the home path. This
|
|
6330
|
-
// is the
|
|
6331
|
-
//
|
|
6332
|
-
// (
|
|
6333
|
-
//
|
|
6334
|
-
//
|
|
6335
|
-
// SPAWNS every server in its config and
|
|
6336
|
-
// only hides tools), so any
|
|
6337
|
-
//
|
|
6338
|
-
//
|
|
6339
|
-
//
|
|
6330
|
+
// is the source of truth for "what MCP servers may an agent-dispatch copilot
|
|
6331
|
+
// load." It is applied to the engine-process copilot spawn paths ONLY — agent
|
|
6332
|
+
// dispatches (`_applyAgentCopilotHome`) and the engine process's own internal
|
|
6333
|
+
// `llm.callLLM` calls — by setting `process.env.COPILOT_HOME` at engine boot
|
|
6334
|
+
// and re-stamping it per dispatch, so those copilot CHILDREN inherit the
|
|
6335
|
+
// filtered config. copilot SPAWNS every server in its config and
|
|
6336
|
+
// authenticates it (`--disable-mcp-server` only hides tools), so any
|
|
6337
|
+
// auth-requiring server pops a Microsoft window per spawn — filtering the
|
|
6338
|
+
// config is the only effective control. Idempotent: writes only when the
|
|
6339
|
+
// content changes (safe under concurrent callers). The operator's real
|
|
6340
|
+
// `~/.copilot` (interactive copilot, and now also Command Center / doc-chat /
|
|
6341
|
+
// the CC worker pool, per W-mre75l6x00024f49) is never touched. Fail-open.
|
|
6340
6342
|
function ensureAgentCopilotHome(minionsDir, engine) {
|
|
6341
6343
|
const home = resolveAgentCopilotHome(minionsDir);
|
|
6342
6344
|
try {
|
|
@@ -7517,6 +7519,19 @@ function getCanonicalPrId(project, prRef, url = '') {
|
|
|
7517
7519
|
return scope ? `${scope}#${prNumber}` : `PR-${prNumber}`;
|
|
7518
7520
|
}
|
|
7519
7521
|
|
|
7522
|
+
function getPrIdentityKey(prRef, project = null) {
|
|
7523
|
+
if (!prRef) return '';
|
|
7524
|
+
const url = typeof prRef === 'object' ? String(prRef.url || '') : '';
|
|
7525
|
+
const rawId = typeof prRef === 'object' ? prRef.id : prRef;
|
|
7526
|
+
const identity = getCanonicalPrId(project, prRef, url) || String(rawId || '');
|
|
7527
|
+
const parsed = parseCanonicalPrId(identity);
|
|
7528
|
+
if (parsed && project && isAdoPrScopeCompatible(parsed.scope, project)) {
|
|
7529
|
+
const projectScope = getProjectPrScope(project);
|
|
7530
|
+
if (projectScope?.startsWith('ado:')) return `${projectScope}#${parsed.prNumber}`;
|
|
7531
|
+
}
|
|
7532
|
+
return identity;
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7520
7535
|
function findPrRecord(prs, prRef, project = null) {
|
|
7521
7536
|
if (!Array.isArray(prs) || !prRef) return null;
|
|
7522
7537
|
const isObjectRef = typeof prRef === 'object';
|
|
@@ -7812,14 +7827,19 @@ function _mergeDuplicatePrInto(winner, loser) {
|
|
|
7812
7827
|
}
|
|
7813
7828
|
}
|
|
7814
7829
|
winner.prdItems = normalizePrLinkItems([...(winner.prdItems || []), ...(loser.prdItems || [])]);
|
|
7830
|
+
if (winner.userDeleted === true || loser.userDeleted === true) {
|
|
7831
|
+
winner.userDeleted = true;
|
|
7832
|
+
if (winner.userDeletedAt == null && loser.userDeletedAt != null) {
|
|
7833
|
+
winner.userDeletedAt = loser.userDeletedAt;
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7815
7836
|
}
|
|
7816
7837
|
|
|
7817
7838
|
/**
|
|
7818
|
-
* P-e9f0a2b4 —
|
|
7819
|
-
*
|
|
7839
|
+
* P-e9f0a2b4 — Repair helper: collapse duplicate records for the same
|
|
7840
|
+
* repository-scoped PR identity within a single tracker scope.
|
|
7820
7841
|
*
|
|
7821
|
-
*
|
|
7822
|
-
* Idempotent — no-op when no duplicates exist.
|
|
7842
|
+
* Called from the reconciliation sweep for every tracker scope. Idempotent.
|
|
7823
7843
|
*
|
|
7824
7844
|
* Selection rule (matching the task spec):
|
|
7825
7845
|
* 1. Prefer `merged` status over any other status.
|
|
@@ -7837,19 +7857,20 @@ function collapseDuplicatePrRecords(prPath, { project = null } = {}) {
|
|
|
7837
7857
|
let collapsed = 0;
|
|
7838
7858
|
mutatePullRequests(prPath, (prs) => {
|
|
7839
7859
|
normalizePrRecords(prs, project);
|
|
7840
|
-
//
|
|
7841
|
-
|
|
7860
|
+
// PR numbers are only unique within a repository. Grouping by number alone
|
|
7861
|
+
// merges unrelated GitHub/ADO repositories and can discard a tombstone.
|
|
7862
|
+
const byIdentity = new Map();
|
|
7842
7863
|
for (const pr of prs) {
|
|
7843
7864
|
if (!pr) continue;
|
|
7844
|
-
const
|
|
7845
|
-
if (
|
|
7846
|
-
if (!
|
|
7847
|
-
|
|
7865
|
+
const identity = getPrIdentityKey(pr, project);
|
|
7866
|
+
if (!identity) continue;
|
|
7867
|
+
if (!byIdentity.has(identity)) byIdentity.set(identity, []);
|
|
7868
|
+
byIdentity.get(identity).push(pr);
|
|
7848
7869
|
}
|
|
7849
7870
|
// Identify loser records to remove (by object reference, not id, because
|
|
7850
7871
|
// normalization may have changed multiple records to the same canonical id).
|
|
7851
7872
|
const toRemove = new Set();
|
|
7852
|
-
for (const [, group] of
|
|
7873
|
+
for (const [, group] of byIdentity) {
|
|
7853
7874
|
if (group.length <= 1) continue;
|
|
7854
7875
|
const best = _pickBestPrRecord(group);
|
|
7855
7876
|
for (const dup of group) {
|
|
@@ -7865,6 +7886,35 @@ function collapseDuplicatePrRecords(prPath, { project = null } = {}) {
|
|
|
7865
7886
|
return { collapsed };
|
|
7866
7887
|
}
|
|
7867
7888
|
|
|
7889
|
+
function _clearPrTombstonesForIdentity(identity) {
|
|
7890
|
+
if (!identity) return 0;
|
|
7891
|
+
const projectsByName = new Map(getProjects().map(project => [project.name, project]));
|
|
7892
|
+
const scopes = new Map();
|
|
7893
|
+
const store = require('./pull-requests-store');
|
|
7894
|
+
for (const pr of store.readAllPullRequests() || []) {
|
|
7895
|
+
if (pr?.userDeleted !== true) continue;
|
|
7896
|
+
const project = pr._scope && pr._scope !== 'central'
|
|
7897
|
+
? projectsByName.get(pr._scope) || null
|
|
7898
|
+
: null;
|
|
7899
|
+
if (getPrIdentityKey(pr, project) !== identity) continue;
|
|
7900
|
+
const prPath = store._filePathForScope(pr._scope || 'central');
|
|
7901
|
+
scopes.set(path.resolve(prPath), { prPath, project });
|
|
7902
|
+
}
|
|
7903
|
+
let cleared = 0;
|
|
7904
|
+
for (const { prPath, project } of scopes.values()) {
|
|
7905
|
+
mutatePullRequests(prPath, (prs) => {
|
|
7906
|
+
for (const pr of prs) {
|
|
7907
|
+
if (pr?.userDeleted !== true || getPrIdentityKey(pr, project) !== identity) continue;
|
|
7908
|
+
delete pr.userDeleted;
|
|
7909
|
+
delete pr.userDeletedAt;
|
|
7910
|
+
cleared++;
|
|
7911
|
+
}
|
|
7912
|
+
return prs;
|
|
7913
|
+
});
|
|
7914
|
+
}
|
|
7915
|
+
return cleared;
|
|
7916
|
+
}
|
|
7917
|
+
|
|
7868
7918
|
/**
|
|
7869
7919
|
* Canonical PR-producing work contract helper.
|
|
7870
7920
|
*
|
|
@@ -7882,17 +7932,15 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
|
|
|
7882
7932
|
...(Array.isArray(itemIds) ? itemIds : [itemId]),
|
|
7883
7933
|
]);
|
|
7884
7934
|
const prNumber = getPrNumber(entry.prNumber ?? entry.id ?? entry.url);
|
|
7885
|
-
//
|
|
7886
|
-
//
|
|
7887
|
-
//
|
|
7888
|
-
//
|
|
7889
|
-
// `
|
|
7890
|
-
//
|
|
7891
|
-
//
|
|
7892
|
-
|
|
7893
|
-
const canonicalId =
|
|
7894
|
-
? getCanonicalPrId(project, entry.id, entry.url || '')
|
|
7895
|
-
: getCanonicalPrId(project, entry.prNumber ?? entry.id ?? entry.url ?? prNumber, entry.url || '');
|
|
7935
|
+
// getPrIdentityKey(entry, project) passes the whole entry object through to
|
|
7936
|
+
// getCanonicalPrId, which (for object refs) reads `entry.id` directly rather
|
|
7937
|
+
// than the `entry.prNumber ?? entry.id ?? ...` coalesce used for bare refs.
|
|
7938
|
+
// That already preserves an already-resolved scope-qualified `entry.id`
|
|
7939
|
+
// (e.g. `ado:office/office/office#5370216`, as enrollPrFromCanonicalId
|
|
7940
|
+
// produces) instead of silently downgrading to a legacy `PR-<n>` stub when
|
|
7941
|
+
// `project` is null/unresolvable — see issue #802 and
|
|
7942
|
+
// test/unit/pr-number-dedup.test.js's "(#802)" case.
|
|
7943
|
+
const canonicalId = getPrIdentityKey(entry, project);
|
|
7896
7944
|
if (!canonicalId) throw new Error('PR id required');
|
|
7897
7945
|
const normalizedEntry = {
|
|
7898
7946
|
...entry,
|
|
@@ -7916,12 +7964,19 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
|
|
|
7916
7964
|
let created = false;
|
|
7917
7965
|
let linked = false;
|
|
7918
7966
|
let skipped = false;
|
|
7919
|
-
let resurrected =
|
|
7967
|
+
let resurrected = allowResurrect && _clearPrTombstonesForIdentity(canonicalId) > 0;
|
|
7920
7968
|
let record = null;
|
|
7921
7969
|
|
|
7922
7970
|
mutatePullRequests(prPath, (prs) => {
|
|
7923
7971
|
normalizePrRecords(prs, project);
|
|
7924
|
-
|
|
7972
|
+
const identityMatches = prs.filter(pr => getPrIdentityKey(pr, project) === canonicalId);
|
|
7973
|
+
let target = identityMatches.length > 0 ? _pickBestPrRecord(identityMatches) : null;
|
|
7974
|
+
if (target && identityMatches.length > 1) {
|
|
7975
|
+
const duplicates = new Set(identityMatches.filter(pr => pr !== target));
|
|
7976
|
+
for (const duplicate of duplicates) _mergeDuplicatePrInto(target, duplicate);
|
|
7977
|
+
prs = prs.filter(pr => !duplicates.has(pr));
|
|
7978
|
+
}
|
|
7979
|
+
const tombstonedMatches = target?.userDeleted === true ? [target] : [];
|
|
7925
7980
|
// Issue #384: tombstoned records (userDeleted:true) must not be re-activated
|
|
7926
7981
|
// by any poller path. Return skipped so reconcilePrs / shared-branch-reconcile
|
|
7927
7982
|
// cannot resurrect a PR the user explicitly removed.
|
|
@@ -7931,13 +7986,15 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
|
|
|
7931
7986
|
// via POST /api/pull-requests/link is explicit intent to bring the PR back.
|
|
7932
7987
|
// Callers that want that behavior opt in with `allowResurrect: true`, which
|
|
7933
7988
|
// clears the tombstone here and lets the normal field-merge logic below run.
|
|
7934
|
-
if (
|
|
7989
|
+
if (tombstonedMatches.length > 0) {
|
|
7935
7990
|
if (!allowResurrect) {
|
|
7936
7991
|
skipped = true;
|
|
7937
7992
|
return prs;
|
|
7938
7993
|
}
|
|
7939
|
-
|
|
7940
|
-
|
|
7994
|
+
for (const match of tombstonedMatches) {
|
|
7995
|
+
delete match.userDeleted;
|
|
7996
|
+
delete match.userDeletedAt;
|
|
7997
|
+
}
|
|
7941
7998
|
resurrected = true;
|
|
7942
7999
|
}
|
|
7943
8000
|
// W-mrduef3q000m2172 — defense-in-depth: `prs` here is scoped to a single
|
|
@@ -7946,37 +8003,23 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
|
|
|
7946
8003
|
// record actually lives in a DIFFERENT scope's file), the above check
|
|
7947
8004
|
// never sees it and would otherwise fall through to insert-as-new,
|
|
7948
8005
|
// silently clearing the tombstone. Check globally before inserting.
|
|
7949
|
-
if (!target) {
|
|
8006
|
+
if (!target && !allowResurrect) {
|
|
7950
8007
|
try {
|
|
7951
8008
|
const store = require('./pull-requests-store');
|
|
7952
|
-
const
|
|
7953
|
-
|
|
8009
|
+
const projectsByName = new Map(getProjects().map(candidate => [candidate.name, candidate]));
|
|
8010
|
+
const globalMatch = (store.readAllPullRequests() || []).find(pr => {
|
|
8011
|
+
if (!pr || pr.userDeleted !== true) return false;
|
|
8012
|
+
const ownerProject = pr._scope && pr._scope !== 'central'
|
|
8013
|
+
? projectsByName.get(pr._scope) || null
|
|
8014
|
+
: null;
|
|
8015
|
+
return getPrIdentityKey(pr, ownerProject) === canonicalId;
|
|
8016
|
+
});
|
|
7954
8017
|
if (globalMatch) {
|
|
7955
8018
|
skipped = true;
|
|
7956
8019
|
return prs;
|
|
7957
8020
|
}
|
|
7958
8021
|
} catch { /* best-effort global tombstone check — fall through on failure */ }
|
|
7959
8022
|
}
|
|
7960
|
-
// P-e9f0a2b4 — When findPrRecord returns null but multiple records share the
|
|
7961
|
-
// same prNumber (e.g. cross-scope contamination or a project-config change
|
|
7962
|
-
// that shifted canonical IDs), collapse the duplicates before inserting.
|
|
7963
|
-
// Without this check, every subsequent upsert would append yet another record
|
|
7964
|
-
// because numberMatches.length > 1 forces findPrRecord to return null.
|
|
7965
|
-
if (!target && prNumber != null) {
|
|
7966
|
-
const numberMatches = prs.filter(pr => pr && !pr.userDeleted && getPrNumber(pr) === prNumber);
|
|
7967
|
-
if (numberMatches.length > 1) {
|
|
7968
|
-
const best = _pickBestPrRecord(numberMatches);
|
|
7969
|
-
const toRemoveIds = new Set(numberMatches.filter(p => p !== best).map(p => p.id));
|
|
7970
|
-
for (const dup of numberMatches) {
|
|
7971
|
-
if (dup === best) continue;
|
|
7972
|
-
_mergeDuplicatePrInto(best, dup);
|
|
7973
|
-
}
|
|
7974
|
-
for (let i = prs.length - 1; i >= 0; i--) {
|
|
7975
|
-
if (prs[i] && toRemoveIds.has(prs[i].id)) prs.splice(i, 1);
|
|
7976
|
-
}
|
|
7977
|
-
target = best; // update the surviving record instead of inserting new
|
|
7978
|
-
}
|
|
7979
|
-
}
|
|
7980
8023
|
if (!target && typeof beforeInsert === 'function' && beforeInsert(prs, normalizedEntry) === false) {
|
|
7981
8024
|
skipped = true;
|
|
7982
8025
|
return prs;
|
|
@@ -9361,6 +9404,7 @@ module.exports = {
|
|
|
9361
9404
|
isPrCompatibleWithProject,
|
|
9362
9405
|
isAdoPrScopeCompatible,
|
|
9363
9406
|
getCanonicalPrId,
|
|
9407
|
+
getPrIdentityKey,
|
|
9364
9408
|
findPrRecord,
|
|
9365
9409
|
isPlaceholderPrTitle,
|
|
9366
9410
|
snapshotPrRecord,
|
package/engine.js
CHANGED
|
@@ -11811,8 +11811,11 @@ if (require.main === module) {
|
|
|
11811
11811
|
// `llm.callLLM` calls (consolidation, classification, meetings, …) — inherits
|
|
11812
11812
|
// this process's COPILOT_HOME, so point it at the seeded, MCP-filtered home.
|
|
11813
11813
|
// Without this, those `direct:true` copilot calls load the operator's full
|
|
11814
|
-
// `~/.copilot` stack and pop a Microsoft auth window per call.
|
|
11815
|
-
//
|
|
11814
|
+
// `~/.copilot` stack and pop a Microsoft auth window per call. This isolation
|
|
11815
|
+
// is engine-process-only: the dashboard process deliberately does NOT set
|
|
11816
|
+
// COPILOT_HOME at its own boot, so Command Center / doc-chat / the CC worker
|
|
11817
|
+
// pool inherit the operator's real `~/.copilot` home instead (reverted in
|
|
11818
|
+
// W-mre75l6x00024f49; see dashboard.js boot for rationale).
|
|
11816
11819
|
// See shared.ensureAgentCopilotHome. Fail-open.
|
|
11817
11820
|
try { process.env.COPILOT_HOME = shared.ensureAgentCopilotHome(MINIONS_DIR, getConfig()?.engine); } catch {}
|
|
11818
11821
|
const { handleCommand } = require('./engine/cli');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2366",
|
|
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"
|