@yemi33/minions 0.1.2448 → 0.1.2449
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/bin/cli-api-client.js +1 -1
- package/bin/install-internal-minions.js +1382 -44
- package/bin/install-layout.js +150 -0
- package/bin/minions.js +460 -167
- package/dashboard/docs/typography.md +65 -12
- package/dashboard/js/command-center.js +58 -4
- package/dashboard/js/detail-panel.js +36 -0
- package/dashboard/js/memory-panel.js +59 -12
- package/dashboard/js/qa.js +179 -20
- package/dashboard/js/refresh.js +148 -12
- package/dashboard/js/render-dispatch.js +3 -4
- package/dashboard/js/render-inbox.js +2 -2
- package/dashboard/js/render-other.js +3 -3
- package/dashboard/js/render-pipelines.js +14 -0
- package/dashboard/js/render-plans.js +57 -9
- package/dashboard/js/render-prd.js +132 -23
- package/dashboard/js/render-prs.js +195 -166
- package/dashboard/js/render-schedules.js +63 -3
- package/dashboard/js/render-utils.js +3 -3
- package/dashboard/js/render-watches.js +19 -3
- package/dashboard/js/render-work-items.js +238 -30
- package/dashboard/js/settings.js +205 -54
- package/dashboard/js/utils.js +51 -1
- package/dashboard/pages/home.html +1 -1
- package/dashboard/pages/qa.html +1 -16
- package/dashboard/pages/work.html +40 -0
- package/dashboard/shared/cc-limits.js +79 -0
- package/dashboard/shared/pr-filters.js +21 -38
- package/dashboard/shared/project-git-summary.js +1 -1
- package/dashboard/shared/record-filters.js +169 -0
- package/dashboard/shared/watches-source.js +1 -1
- package/dashboard/shared/welcome-popup.js +1 -1
- package/dashboard/shared/wi-filters.js +302 -0
- package/dashboard/slim/body.html +1 -0
- package/dashboard/slim/js/command-send.js +26 -0
- package/dashboard/slim/js/modals-tiles.js +380 -39
- package/dashboard/slim/js/status.js +13 -21
- package/dashboard/slim/layout.html +1 -0
- package/dashboard/slim/panel-bootstrap.js +6 -2
- package/dashboard/slim/styles.css +38 -0
- package/dashboard/styles.css +159 -55
- package/dashboard-build.js +13 -2
- package/dashboard.js +956 -423
- package/docs/README.md +11 -6
- package/docs/api-errors.md +2 -2
- package/docs/architecture-review-2026-07-09.md +1 -1
- package/docs/architecture.excalidraw +2 -2
- package/docs/auto-discovery.md +18 -9
- package/docs/branch-derivation.md +4 -4
- package/docs/capture-demos.js +39 -2
- package/docs/ci-runner-canary.md +123 -0
- package/docs/claude-md-propagation.md +2 -2
- package/docs/cloud-agent-dispatch.md +204 -0
- package/docs/command-center.md +7 -7
- package/docs/completion-reports.md +43 -20
- package/docs/constants.md +10 -3
- package/docs/constellation-bridge.md +134 -6
- package/docs/constellation-style-telemetry.md +4 -4
- package/docs/contracts/capability-protocol.v1.json +165 -0
- package/docs/cooldown-merge-semantics.md +12 -12
- package/docs/copilot-cli-schema.md +7 -7
- package/docs/cross-repo-plans.md +10 -10
- package/docs/dead-code-audit-retractions.md +5 -5
- package/docs/default-branch-ci.md +173 -0
- package/docs/deprecated.json +31 -31
- package/docs/design-inbox-entries-schema.md +3 -3
- package/docs/design-language.md +1051 -0
- package/docs/design-state-storage.md +11 -11
- package/docs/diagnostics-crash-reports.md +9 -9
- package/docs/diagnostics-memory.md +5 -5
- package/docs/documentation-audit-2026-07-09.md +7 -7
- package/docs/engine-restart.md +90 -5
- package/docs/harness-mode.md +1 -1
- package/docs/internal-install.md +338 -39
- package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
- package/docs/kb-pr3223-cascade-archiving.md +1 -1
- package/docs/kb-pr696-merge-conflict-docs.md +6 -6
- package/docs/kb-sweep.md +35 -35
- package/docs/keep-processes.md +1 -1
- package/docs/live-checkout-mode.md +30 -30
- package/docs/managed-spawn.md +18 -14
- package/docs/named-agents.md +7 -7
- package/docs/plan-lifecycle.md +69 -2
- package/docs/pr-author-identity.md +114 -0
- package/docs/pr-auto-fix-dispatch.md +19 -4
- package/docs/pr-comment-followup.md +6 -6
- package/docs/pr-review-fix-loop.md +59 -10
- package/docs/process-termination.md +40 -0
- package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
- package/docs/qa-runbook-lifecycle.md +367 -17
- package/docs/qa-runbooks.md +3 -3
- package/docs/rfc-completion-json.md +18 -18
- package/docs/runtime-adapters.md +26 -21
- package/docs/security.md +6 -6
- package/docs/self-improvement.md +4 -4
- package/docs/shared-lifecycle-module-map.md +473 -472
- package/docs/skills.md +52 -3
- package/docs/slim-ux/concepts.md +121 -116
- package/docs/specs/agent-configurability.md +18 -18
- package/docs/specs/agent-rename.md +18 -18
- package/docs/team-memory.md +38 -21
- package/docs/timeouts-and-liveness.md +118 -10
- package/docs/tutorials/01-install-and-connect.md +1 -1
- package/docs/watches.md +40 -39
- package/docs/workspace-manifests.md +4 -4
- package/docs/worktree-lifecycle.md +293 -14
- package/engine/README.md +46 -0
- package/engine/{ado-comment.js → ado/comment.js} +8 -8
- package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
- package/engine/{ado.js → ado/index.js} +417 -63
- package/engine/{ado-status.js → ado/status.js} +6 -8
- package/engine/{ado-token.js → ado/token.js} +1 -1
- package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
- package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
- package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
- package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
- package/engine/{harness-context.js → agents/harness-context.js} +5 -5
- package/engine/{harness.js → agents/harness.js} +3 -3
- package/engine/{llm.js → agents/llm.js} +18 -14
- package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
- package/engine/{playbook.js → agents/playbook.js} +155 -22
- package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
- package/engine/{preflight.js → agents/preflight.js} +29 -10
- package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
- package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
- package/engine/{steering.js → agents/steering.js} +5 -5
- package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
- package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
- package/engine/{api-validation.js → api/api-validation.js} +1 -1
- package/engine/api/bridge.js +787 -0
- package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
- package/engine/api/companion.js +560 -0
- package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
- package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
- package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
- package/engine/api-contracts/agent-content.js +4 -4
- package/engine/api-contracts/capability-manifest.js +236 -0
- package/engine/api-contracts/capability-protocol.js +333 -0
- package/engine/api-contracts/cc-ops.js +1 -1
- package/engine/api-contracts/config-runtime.js +5 -0
- package/engine/api-contracts/core.js +28 -1
- package/engine/api-contracts/index.js +100 -0
- package/engine/api-contracts/orchestration.js +18 -5
- package/engine/api-contracts/pull-requests.js +37 -6
- package/engine/api-contracts/qa-process.js +29 -6
- package/engine/api-contracts/work-plan-prd.js +21 -1
- package/engine/cloud/contract.js +212 -0
- package/engine/cloud/index.js +159 -0
- package/engine/{execution-model.js → core/execution-model.js} +1 -1
- package/engine/{features.js → core/features.js} +4 -4
- package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
- package/engine/{queries.js → core/queries.js} +201 -36
- package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
- package/engine/{shared.js → core/shared.js} +1637 -175
- package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
- package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
- package/engine/db/index.js +11 -2
- package/engine/db/migrations/002-dispatches.js +3 -3
- package/engine/db/migrations/003-work-items.js +1 -1
- package/engine/db/migrations/004-pull-requests.js +1 -1
- package/engine/db/migrations/006-metrics.js +1 -1
- package/engine/db/migrations/007-watches.js +2 -2
- package/engine/db/migrations/008-small-state.js +1 -1
- package/engine/db/migrations/009-qa.js +1 -1
- package/engine/db/migrations/010-pr-links.js +1 -1
- package/engine/db/migrations/011-remaining-state.js +1 -1
- package/engine/db/migrations/012-steering-deliveries.js +2 -2
- package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
- package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
- package/engine/db/migrations/015-plans-prds.js +0 -0
- package/engine/db/migrations/018-sql-only-cutover.js +2 -2
- package/engine/db/migrations/021-archived-work-items.js +1 -1
- package/engine/db/migrations/022-global-cc-session.js +1 -1
- package/engine/db/migrations/023-engine-state.js +1 -1
- package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
- package/engine/db/migrations/029-repair-reused-versions.js +20 -0
- package/engine/db/migrations/031-pr-author-identity.js +137 -0
- package/engine/{consolidation.js → memory/consolidation.js} +6 -6
- package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
- package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
- package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
- package/engine/{memory-store.js → memory/memory-store.js} +3 -3
- package/engine/{promotion.js → memory/promotion.js} +3 -3
- package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
- package/engine/{review-learning.js → memory/review-learning.js} +10 -5
- package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
- package/engine/{logs-store.js → observability/logs-store.js} +5 -5
- package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
- package/engine/{check-status.js → operations/check-status.js} +3 -3
- package/engine/{cli.js → operations/cli.js} +271 -113
- package/engine/{distribution.js → operations/distribution.js} +5 -6
- package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
- package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
- package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
- package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
- package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
- package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
- package/engine/{meeting.js → orchestration/meeting.js} +6 -16
- package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
- package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
- package/engine/{routing.js → orchestration/routing.js} +3 -3
- package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
- package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
- package/engine/{timeout.js → orchestration/timeout.js} +158 -109
- package/engine/{db-events.js → persistence/db-events.js} +2 -2
- package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
- package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
- package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
- package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
- package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
- package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
- package/engine/persistence/state-operations.js +350 -0
- package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
- package/engine/{issues.js → planning/issues.js} +2 -2
- package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
- package/engine/planning/prd-result-sidecar.js +190 -0
- package/engine/{prd-store.js → planning/prd-store.js} +17 -17
- package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
- package/engine/{projects.js → planning/projects.js} +10 -10
- package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
- package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
- package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
- package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
- package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
- package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
- package/engine/{process-utils.js → processes/process-utils.js} +599 -55
- package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
- package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
- package/engine/{comment-format.js → providers/comment-format.js} +5 -5
- package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
- package/engine/{gh-token.js → providers/gh-token.js} +4 -4
- package/engine/{github.js → providers/github.js} +131 -54
- package/engine/{pr-action.js → providers/pr-action.js} +13 -12
- package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
- package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
- package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
- package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
- package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
- package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
- package/engine/{pr-track.js → providers/pr-track.js} +11 -13
- package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
- package/engine/qa/auto-prd-qa.js +313 -0
- package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
- package/engine/qa/prd-session.js +240 -0
- package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
- package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
- package/engine/{qa-runs.js → qa/runs.js} +286 -15
- package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
- package/engine/qa/visual-journey.js +654 -0
- package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
- package/engine/qa-runners/maestro.js +3 -3
- package/engine/qa-runners/playwright.js +2 -2
- package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
- package/engine/recovery/stop-stack.js +607 -0
- package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
- package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
- package/engine/runtimes/claude.js +14 -12
- package/engine/runtimes/codex.js +8 -6
- package/engine/runtimes/copilot.js +17 -16
- package/engine/{watch-actions.js → watches/actions.js} +13 -13
- package/engine/{watches.js → watches/index.js} +43 -32
- package/engine/{watches-store.js → watches/store.js} +4 -4
- package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
- package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
- package/engine/worktrees/inventory.js +671 -0
- package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
- package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
- package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
- package/engine/worktrees/quarantine-refs.js +173 -0
- package/engine.js +1137 -208
- package/minions.js +147 -77
- package/package.json +10 -6
- package/playbooks/_pr-description-audit.md +110 -78
- package/playbooks/build-fix-complex.md +2 -0
- package/playbooks/fix.md +16 -12
- package/playbooks/implement-shared.md +2 -0
- package/playbooks/implement.md +19 -20
- package/playbooks/plan-to-prd.md +18 -3
- package/playbooks/qa-session-draft.md +136 -1
- package/playbooks/qa-session-execute.md +80 -2
- package/playbooks/qa-session-setup.md +17 -1
- package/playbooks/qa-validate.md +1 -1
- package/playbooks/setup.md +2 -0
- package/playbooks/shared-rules.md +25 -32
- package/playbooks/templates/followup-dispatch.md +4 -3
- package/playbooks/verify.md +1 -1
- package/prompts/cc-system.md +19 -27
- package/watch-plugins/README.md +92 -0
- package/watch-plugins/ado-author-prs.js +336 -0
- package/watch-plugins/gh-author-prs.js +375 -0
- package/watch-plugins/http.js +474 -0
- package/watch-plugins/teams-channel.js +869 -0
- package/docs/dev-composite-workflow.md +0 -101
- package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
- package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
- package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/visual-evidence-ci.md +0 -103
- package/engine/bridge.js +0 -379
- package/engine/quarantine-refs.js +0 -103
- package/engine/state-operations.js +0 -178
- /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
package/minions.js
CHANGED
|
@@ -16,10 +16,10 @@ const fs = require('fs');
|
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const readline = require('readline');
|
|
18
18
|
const { execSync } = require('child_process');
|
|
19
|
-
const shared = require('./engine/shared');
|
|
19
|
+
const shared = require('./engine/core/shared');
|
|
20
20
|
const { ENGINE_DEFAULTS, DEFAULT_AGENTS } = shared;
|
|
21
|
-
const projectDiscovery = require('./engine/project-discovery');
|
|
22
|
-
const scheduleBootstrap = require('./engine/schedule-bootstrap');
|
|
21
|
+
const projectDiscovery = require('./engine/planning/project-discovery');
|
|
22
|
+
const scheduleBootstrap = require('./engine/orchestration/schedule-bootstrap');
|
|
23
23
|
|
|
24
24
|
const MINIONS_HOME = process.env.MINIONS_HOME ? path.resolve(process.env.MINIONS_HOME) : __dirname;
|
|
25
25
|
const CONFIG_PATH = path.join(MINIONS_HOME, 'config.json');
|
|
@@ -41,8 +41,34 @@ function cleanupPlaceholderProjects(config) {
|
|
|
41
41
|
return removed;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const EMPTY_CONFIG = { projects: [], engine: {}, claude: {}, agents: {} };
|
|
45
|
+
function emptyConfig() { return JSON.parse(JSON.stringify(EMPTY_CONFIG)); }
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Persist a config.json change through the shared file lock, applying the
|
|
49
|
+
* mutation to the FRESH on-disk snapshot (W-ms811bme000n475a).
|
|
50
|
+
*
|
|
51
|
+
* The previous `loadConfig()` … whole-config `fs.writeFileSync` pattern wrote a
|
|
52
|
+
* stale config object: anything another writer committed between the read and the
|
|
53
|
+
* write was silently clobbered. `minions init` runs on every `minions update`
|
|
54
|
+
* and rewrites `projects[]` it never intended to touch, so a project removed
|
|
55
|
+
* from the dashboard in that window came straight back. This mirrors the same
|
|
56
|
+
* fix already applied to removeProject (P-bf05-config-write-race).
|
|
57
|
+
*
|
|
58
|
+
* The mutator MUST stay synchronous and fast — it runs while the config lock is
|
|
59
|
+
* held. Do prompts, runtime detection, and filesystem setup outside it. It may
|
|
60
|
+
* also be re-run if the lock is compromised, so keep it idempotent.
|
|
61
|
+
*/
|
|
62
|
+
function mutateConfig(mutator) {
|
|
63
|
+
return shared.mutateJsonFileLocked(CONFIG_PATH, (config) => {
|
|
64
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) config = emptyConfig();
|
|
65
|
+
const next = mutator(config);
|
|
66
|
+
return next === undefined ? config : next;
|
|
67
|
+
}, { defaultValue: emptyConfig() });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _sameLocalPath(entry, resolvedTarget) {
|
|
71
|
+
return !!(entry && entry.localPath) && path.resolve(entry.localPath) === resolvedTarget;
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -99,16 +125,13 @@ async function addProject(targetDir) {
|
|
|
99
125
|
process.exit(1);
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// Check if already linked
|
|
106
|
-
const existing = config.projects.find(p => path.resolve(p.localPath) === target);
|
|
128
|
+
// Read-only probe for the "already linked" prompt. The authoritative
|
|
129
|
+
// read-modify-write happens under the config lock further down.
|
|
130
|
+
const existing = (loadConfig().projects || []).find(p => _sameLocalPath(p, target));
|
|
107
131
|
if (existing) {
|
|
108
132
|
console.log(` "${existing.name}" is already linked at ${target}`);
|
|
109
133
|
const update = await ask('Update its configuration? (y/N)', 'N');
|
|
110
134
|
if (update.toLowerCase() !== 'y') { rl.close(); return; }
|
|
111
|
-
config.projects = config.projects.filter(p => path.resolve(p.localPath) !== target);
|
|
112
135
|
}
|
|
113
136
|
|
|
114
137
|
console.log('\n Link Project to Minions');
|
|
@@ -154,7 +177,7 @@ async function addProject(targetDir) {
|
|
|
154
177
|
// missing fields up front rather than letting the CLI silently persist a
|
|
155
178
|
// broken project.
|
|
156
179
|
try {
|
|
157
|
-
require('./engine/projects').assertProjectLinkOk(projectEntry);
|
|
180
|
+
require('./engine/planning/projects').assertProjectLinkOk(projectEntry);
|
|
158
181
|
} catch (e) {
|
|
159
182
|
console.log(`\n Error: ${e.message}`);
|
|
160
183
|
if (Array.isArray(e.missing) && e.missing.length > 0) {
|
|
@@ -164,18 +187,28 @@ async function addProject(targetDir) {
|
|
|
164
187
|
}
|
|
165
188
|
|
|
166
189
|
shared.initializeProjectState(projectEntry);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
// Re-apply against the FRESH on-disk config: the interactive prompts above
|
|
191
|
+
// can be open for minutes, and a dashboard/CLI project removal that landed in
|
|
192
|
+
// that window must not be undone by this write (W-ms811bme000n475a).
|
|
193
|
+
let totalProjects = 0;
|
|
194
|
+
mutateConfig(cfg => {
|
|
195
|
+
if (!Array.isArray(cfg.projects)) cfg.projects = [];
|
|
196
|
+
const prior = cfg.projects.find(p => _sameLocalPath(p, target));
|
|
197
|
+
cfg.projects = cfg.projects.filter(p => !_sameLocalPath(p, target));
|
|
198
|
+
if (prior && String(prior.name || '').toLowerCase() !== String(projectEntry.name || '').toLowerCase()) {
|
|
199
|
+
scheduleBootstrap.disableProjectGitFetchSchedules(cfg, prior.name);
|
|
200
|
+
}
|
|
201
|
+
cfg.projects.push(projectEntry);
|
|
202
|
+
scheduleBootstrap.applyScheduleBootstrap(cfg, {
|
|
203
|
+
projectNames: [projectEntry.name],
|
|
204
|
+
includeHarnessSweep: true,
|
|
205
|
+
});
|
|
206
|
+
totalProjects = cfg.projects.length;
|
|
207
|
+
return cfg;
|
|
174
208
|
});
|
|
175
|
-
saveConfig(config);
|
|
176
209
|
|
|
177
210
|
console.log(`\n Linked "${name}" (${target})`);
|
|
178
|
-
console.log(` Total projects: ${
|
|
211
|
+
console.log(` Total projects: ${totalProjects}`);
|
|
179
212
|
console.log(`\n Start the minions from anywhere:`);
|
|
180
213
|
console.log(` node ${MINIONS_HOME}/engine.js # Engine`);
|
|
181
214
|
console.log(` node ${MINIONS_HOME}/dashboard.js # Dashboard`);
|
|
@@ -183,7 +216,7 @@ async function addProject(targetDir) {
|
|
|
183
216
|
}
|
|
184
217
|
|
|
185
218
|
function removeProject(target, options = {}) {
|
|
186
|
-
const { removeProject: doRemove } = require('./engine/projects');
|
|
219
|
+
const { removeProject: doRemove } = require('./engine/planning/projects');
|
|
187
220
|
// CLI accepts a path; project lookup also matches by name as a convenience
|
|
188
221
|
const result = doRemove(path.resolve(target), options);
|
|
189
222
|
if (!result.ok) {
|
|
@@ -315,8 +348,10 @@ async function scanAndAdd({ root, depth } = {}) {
|
|
|
315
348
|
return;
|
|
316
349
|
}
|
|
317
350
|
|
|
318
|
-
|
|
319
|
-
|
|
351
|
+
// Read-only snapshot used to tag "already linked" in the listing. The
|
|
352
|
+
// authoritative read-modify-write happens under the config lock further down.
|
|
353
|
+
const linkedPaths = new Set(
|
|
354
|
+
(loadConfig().projects || []).filter(p => p && p.localPath).map(p => path.resolve(p.localPath)));
|
|
320
355
|
|
|
321
356
|
// Enrich repos with auto-discovered metadata
|
|
322
357
|
const enriched = repos.map(repoPath => {
|
|
@@ -376,34 +411,57 @@ async function scanAndAdd({ root, depth } = {}) {
|
|
|
376
411
|
|
|
377
412
|
console.log(`\n Adding ${toAdd.length} project(s)...\n`);
|
|
378
413
|
|
|
379
|
-
|
|
414
|
+
// Re-apply against the FRESH on-disk config — the selection prompt above can
|
|
415
|
+
// be open for minutes (W-ms811bme000n475a). Name/path dedupe therefore runs
|
|
416
|
+
// inside the lock, and initializeProjectState (filesystem work) runs after it.
|
|
380
417
|
const addedProjectNames = [];
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
418
|
+
const addedEntries = [];
|
|
419
|
+
let totalProjects = 0;
|
|
420
|
+
mutateConfig(cfg => {
|
|
421
|
+
if (!Array.isArray(cfg.projects)) cfg.projects = [];
|
|
422
|
+
// The mutator can be re-run when the lock is compromised — reset accumulators.
|
|
423
|
+
addedProjectNames.length = 0;
|
|
424
|
+
addedEntries.length = 0;
|
|
425
|
+
const existingNames = new Set(cfg.projects.map(p => p && p.name).filter(Boolean));
|
|
426
|
+
const existingPaths = new Set(
|
|
427
|
+
cfg.projects.filter(p => p && p.localPath).map(p => path.resolve(p.localPath)));
|
|
428
|
+
for (const repo of toAdd) {
|
|
429
|
+
// Another writer may have linked this path since the scan listing.
|
|
430
|
+
if (existingPaths.has(path.resolve(repo.path))) continue;
|
|
431
|
+
// Deduplicate names — append -2, -3 etc. if name already taken
|
|
432
|
+
let name = repo.name;
|
|
433
|
+
if (existingNames.has(name)) {
|
|
434
|
+
let i = 2;
|
|
435
|
+
while (existingNames.has(name + '-' + i)) i++;
|
|
436
|
+
name = name + '-' + i;
|
|
437
|
+
}
|
|
438
|
+
existingNames.add(name);
|
|
439
|
+
existingPaths.add(path.resolve(repo.path));
|
|
440
|
+
const projectEntry = buildProjectEntry({
|
|
441
|
+
name, description: repo.description, localPath: repo.path,
|
|
442
|
+
repoHost: repo.host, repositoryId: repo.repositoryId, org: repo.org, project: repo.project,
|
|
443
|
+
repoName: repo.repoName, mainBranch: repo.mainBranch, prUrlBase: repo.prUrlBase,
|
|
444
|
+
});
|
|
445
|
+
cfg.projects.push(projectEntry);
|
|
446
|
+
addedProjectNames.push(projectEntry.name);
|
|
447
|
+
addedEntries.push(projectEntry);
|
|
388
448
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
449
|
+
if (addedProjectNames.length > 0) {
|
|
450
|
+
scheduleBootstrap.applyScheduleBootstrap(cfg, {
|
|
451
|
+
projectNames: addedProjectNames,
|
|
452
|
+
includeHarnessSweep: true,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
totalProjects = cfg.projects.length;
|
|
456
|
+
return cfg;
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
for (const entry of addedEntries) {
|
|
460
|
+
shared.initializeProjectState(entry);
|
|
461
|
+
console.log(` + ${entry.name} (${entry.localPath})`);
|
|
399
462
|
}
|
|
400
463
|
|
|
401
|
-
|
|
402
|
-
projectNames: addedProjectNames,
|
|
403
|
-
includeHarnessSweep: true,
|
|
404
|
-
});
|
|
405
|
-
saveConfig(config);
|
|
406
|
-
console.log(`\n Done. ${config.projects.length} total project(s) linked.`);
|
|
464
|
+
console.log(`\n Done. ${totalProjects} total project(s) linked.`);
|
|
407
465
|
console.log(` Run "minions list" to verify.\n`);
|
|
408
466
|
rl.close();
|
|
409
467
|
}
|
|
@@ -413,39 +471,51 @@ async function scanAndAdd({ root, depth } = {}) {
|
|
|
413
471
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
414
472
|
|
|
415
473
|
async function initMinions({ skipScan = false, scanRoot, scanDepth } = {}) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
474
|
+
// Runtime detection shells out (`where` / `which`), so it must run OUTSIDE the
|
|
475
|
+
// config lock — lock callbacks stay synchronous and fast.
|
|
476
|
+
const hadConfiguredDefaultCli = loadConfig().engine?.defaultCli !== undefined;
|
|
477
|
+
const detected = hadConfiguredDefaultCli ? [] : _detectAvailableRuntimes();
|
|
478
|
+
|
|
479
|
+
// W-ms811bme000n475a: merge into the FRESH on-disk config under the lock.
|
|
480
|
+
// `minions init` runs on every `minions update`; the previous stale-snapshot
|
|
481
|
+
// rewrite resurrected projects that were unlinked while it was running, even
|
|
482
|
+
// though init never intends to touch `projects[]`.
|
|
483
|
+
let removedPlaceholders = 0;
|
|
484
|
+
let pinnedDefaultCli = null;
|
|
485
|
+
mutateConfig(config => {
|
|
486
|
+
removedPlaceholders = 0;
|
|
487
|
+
pinnedDefaultCli = null;
|
|
488
|
+
if (!config.projects) config.projects = [];
|
|
489
|
+
removedPlaceholders = cleanupPlaceholderProjects(config);
|
|
490
|
+
// Merge defaults — fills in new fields from upgrades while preserving user customizations
|
|
491
|
+
if (!config.engine) config.engine = {};
|
|
492
|
+
for (const [k, v] of Object.entries(ENGINE_DEFAULTS)) {
|
|
493
|
+
if (k === 'defaultCli') continue;
|
|
494
|
+
if (config.engine[k] === undefined) config.engine[k] = v;
|
|
495
|
+
}
|
|
496
|
+
shared.pruneDefaultClaudeConfig(config);
|
|
497
|
+
if (!config.agents || Object.keys(config.agents).length === 0) {
|
|
498
|
+
config.agents = { ...DEFAULT_AGENTS };
|
|
499
|
+
}
|
|
430
500
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
// Prefer the canonical fleet default when it is installed.
|
|
441
|
-
config.engine.defaultCli = detected.includes(ENGINE_DEFAULTS.defaultCli)
|
|
442
|
-
? ENGINE_DEFAULTS.defaultCli
|
|
443
|
-
: detected[0];
|
|
444
|
-
console.log(`\n ✓ Detected ${detected.join(' + ')} — fleet default set to ${config.engine.defaultCli}`);
|
|
501
|
+
// Auto-detect available runtime CLIs and pin engine.defaultCli to whichever
|
|
502
|
+
// is installed. Only set if the user hasn't already configured one — never
|
|
503
|
+
// overwrite an explicit choice on `init --force` upgrades. Re-checked here
|
|
504
|
+
// because another writer may have pinned it since the pre-lock probe.
|
|
505
|
+
if (config.engine.defaultCli === undefined && detected.length > 0) {
|
|
506
|
+
config.engine.defaultCli = detected.length === 1
|
|
507
|
+
? detected[0]
|
|
508
|
+
: (detected.includes(ENGINE_DEFAULTS.defaultCli) ? ENGINE_DEFAULTS.defaultCli : detected[0]);
|
|
509
|
+
pinnedDefaultCli = config.engine.defaultCli;
|
|
445
510
|
}
|
|
446
511
|
// If nothing is detected, leave defaultCli unset so the engine uses its canonical default.
|
|
512
|
+
return config;
|
|
513
|
+
});
|
|
514
|
+
if (pinnedDefaultCli) {
|
|
515
|
+
console.log(detected.length === 1
|
|
516
|
+
? `\n ✓ Detected ${detected[0]} CLI — set as fleet default runtime`
|
|
517
|
+
: `\n ✓ Detected ${detected.join(' + ')} — fleet default set to ${pinnedDefaultCli}`);
|
|
447
518
|
}
|
|
448
|
-
saveConfig(config);
|
|
449
519
|
console.log(`\n Minions initialized at ${MINIONS_HOME}`);
|
|
450
520
|
console.log(` Config, agents, and engine defaults created.\n`);
|
|
451
521
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2449",
|
|
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"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"package:check": "node
|
|
10
|
-
"package:prepare:public": "node
|
|
11
|
-
"package:prepare:internal": "node
|
|
9
|
+
"package:check": "node scripts/check-package-boundary.js public",
|
|
10
|
+
"package:prepare:public": "node scripts/prepare-package.js public",
|
|
11
|
+
"package:prepare:internal": "node scripts/prepare-package.js internal",
|
|
12
|
+
"package:drift:check": "node scripts/reconcile-publish-drift.js --check",
|
|
13
|
+
"package:drift:apply": "node scripts/reconcile-publish-drift.js --apply",
|
|
12
14
|
"dev": "node bin/minions.js --dev restart",
|
|
13
15
|
"dev:start": "node bin/minions.js --dev start",
|
|
14
16
|
"dev:stop": "node bin/minions.js --dev stop",
|
|
@@ -50,6 +52,7 @@
|
|
|
50
52
|
"dashboard-build.js",
|
|
51
53
|
"dashboard.js",
|
|
52
54
|
"docs/",
|
|
55
|
+
"engine/README.md",
|
|
53
56
|
"engine/**/*.js",
|
|
54
57
|
"engine.js",
|
|
55
58
|
"knowledge/agents/*.md",
|
|
@@ -57,7 +60,8 @@
|
|
|
57
60
|
"playbooks/",
|
|
58
61
|
"prompts/",
|
|
59
62
|
"routing.md",
|
|
60
|
-
"skills/"
|
|
63
|
+
"skills/",
|
|
64
|
+
"watch-plugins/"
|
|
61
65
|
],
|
|
62
66
|
"repository": {
|
|
63
67
|
"type": "git",
|
|
@@ -69,7 +73,7 @@
|
|
|
69
73
|
},
|
|
70
74
|
"devDependencies": {
|
|
71
75
|
"@playwright/test": "^1.58.2",
|
|
72
|
-
"eslint": "^
|
|
76
|
+
"eslint": "^10.8.0",
|
|
73
77
|
"eslint-plugin-no-unsanitized": "^4.1.5"
|
|
74
78
|
},
|
|
75
79
|
"publishConfig": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PR description audit + screenshot refresh (shared fragment)
|
|
2
2
|
|
|
3
3
|
> **Naming convention.** Files named `_*.md` under `playbooks/` are **shared
|
|
4
|
-
> fragments**. The engine renderer (`engine/playbook.js`) does not (yet)
|
|
4
|
+
> fragments**. The engine renderer (`engine/agents/playbook.js`) does not (yet)
|
|
5
5
|
> implement an `<include>` directive, so each consuming playbook inlines this
|
|
6
6
|
> fragment verbatim and the two copies are kept in sync by hand. If you change
|
|
7
7
|
> this file, also update the inlined copies in `playbooks/fix.md` and
|
|
@@ -65,108 +65,137 @@ When you skip, still report it: set `meta.descriptionAudit.ran = false` and
|
|
|
65
65
|
6. **GET-verify the description post-patch** by re-running the fetch from
|
|
66
66
|
step 1 and confirming the body matches what you sent.
|
|
67
67
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
## Visual evidence handling — screenshots + recordings (introduce AND refresh for visual changes)
|
|
69
|
+
|
|
70
|
+
Visual evidence is a **default behavior for UI changes on ANY project**, not a
|
|
71
|
+
minions-only nicety. For a PR that contains a **meaningful visual/UI change** —
|
|
72
|
+
web front-ends, dashboards, mobile webviews, or any rendered surface a user sees
|
|
73
|
+
— proactively CAPTURE evidence and EMBED it directly in the PR description; do
|
|
74
|
+
**not** rely only on the OPG `Visual evidence capture` CI bot (that bot stays
|
|
75
|
+
as-is and is minions-specific; this is additive, cross-project PR-body evidence).
|
|
76
|
+
When the existing description already embeds `` refs for a view this
|
|
77
|
+
dispatch touched, refresh them the same way. Decide "is this a visual change?"
|
|
78
|
+
from whether the diff changes a **rendered UI surface**, NOT from repo-specific
|
|
79
|
+
paths.
|
|
80
|
+
|
|
81
|
+
Capture **both** where applicable:
|
|
82
|
+
|
|
83
|
+
- **Screenshots** — a BEFORE/AFTER pair for visual FIXES; an AFTER-only shot for
|
|
84
|
+
NEW UI.
|
|
85
|
+
- **Screen recordings / video** — for interactive, animated, or multi-step flows
|
|
86
|
+
(hover/expand, drag, wizard steps, transitions). Playwright records video per
|
|
87
|
+
browser context; save the resulting file alongside the screenshots.
|
|
88
|
+
|
|
89
|
+
**How to capture — resolution order (harness first, then the minions skill).**
|
|
90
|
+
|
|
91
|
+
1. **Harness first.** Consult the TARGET repo's own instructions for how it runs
|
|
92
|
+
its UI and captures evidence before inventing a command: `AGENTS.md`,
|
|
93
|
+
`CLAUDE.md`, `README`/`CONTRIBUTING`, a project-local playbook override under
|
|
94
|
+
`projects/<name>/playbooks/`, or a project-embedded skill under
|
|
95
|
+
`.claude/skills` / `.github/skills` / `.agents/skills`. The engine already
|
|
96
|
+
surfaces this harness plumbing read-only — `engine/agents/harness-context.js` and
|
|
97
|
+
`GET /api/harness/diagnostics` — so ground your capture on what the repo
|
|
98
|
+
actually declares; don't guess.
|
|
99
|
+
2. **Fall back to the minions Playwright skill.** If the target repo has no
|
|
100
|
+
visual-evidence instructions, use Minions' own project-agnostic
|
|
101
|
+
`capture-web-evidence` skill (`.claude/skills/capture-web-evidence/SKILL.md`),
|
|
102
|
+
which drives Playwright (Playwright MCP when available, else the Playwright
|
|
103
|
+
CLI) against any web or mobile-webview UI.
|
|
75
104
|
|
|
76
105
|
**MANDATORY GATE — UI-surface diffs REQUIRE screenshots.** Screenshot capture +
|
|
77
106
|
embed is a **required completion gate**, not a nicety, for any dispatch whose diff
|
|
78
107
|
modifies a **rendered UI surface** — e.g. files under `dashboard/`,
|
|
79
108
|
`dashboard/slim/`, `dashboard/pages/`, `dashboard/js/`, `dashboard/styles.css`, or
|
|
80
109
|
ANY change that alters what a dashboard page/route renders
|
|
81
|
-
(layout/CSS/markup/new UI).
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
(layout/CSS/markup/new UI). This is the minions-dashboard instance of the
|
|
111
|
+
cross-project default above and is **not** weakened by it. For such a diff you
|
|
112
|
+
**MUST** capture and embed AFTER screenshots (BEFORE/AFTER for visual FIXES,
|
|
113
|
+
AFTER-only for NEW UI) for the PR to be considered complete. Omitting screenshots
|
|
114
|
+
on a UI-surface diff **without a recorded hard-failure reason makes the PR
|
|
115
|
+
incomplete** — "I judged it not visual enough" is **NOT** a valid skip when the
|
|
116
|
+
diff touches those paths. The ONLY legitimate skips are genuine hard failures: the
|
|
117
|
+
dev server will not start after a real attempt, Playwright MCP is unavailable, the
|
|
118
|
+
route 404s, or upload/attachment auth fails. Record any such skip in
|
|
119
|
+
`meta.descriptionAudit.result` as `screenshots-skipped (<specific-reason>)`.
|
|
120
|
+
|
|
121
|
+
**Scope guard — which changes get visual evidence.** Only meaningful visual/UI
|
|
91
122
|
changes: dashboard pages, slim-ux, Settings, work-item/PR/plan views, badges,
|
|
92
|
-
layout/CSS
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
123
|
+
layout/CSS, and the equivalent rendered surfaces of other projects.
|
|
124
|
+
Skip backend-only, engine-logic, text/prose-only, analysis, and docs changes —
|
|
125
|
+
those get NO screenshots or recordings (they add no signal). Use a BEFORE/AFTER
|
|
126
|
+
pair for layout/visual FIXES; an AFTER-only shot for NEW UI; add a recording only
|
|
127
|
+
for interactive/animated flows.
|
|
128
|
+
|
|
129
|
+
**Capture.** Detect the project's run command generically — from the repo harness
|
|
130
|
+
(step 1 above) or from a `package.json` script named `dev`, `start`, or `serve`
|
|
131
|
+
(or the repo's documented preview command). Do **not** hardcode a per-repo build
|
|
132
|
+
command. With Playwright available:
|
|
133
|
+
|
|
134
|
+
1. Spin up the project's dev/preview server with a detached handoff (see
|
|
135
|
+
`shared-rules.md` → "Long-Running Commands"). Record PID + log path + URL +
|
|
136
|
+
stop command.
|
|
137
|
+
2. Drive Playwright to the changed route/view at a **normal desktop viewport**;
|
|
138
|
+
for **mobile webviews**, also capture at a mobile device-emulation viewport
|
|
139
|
+
(e.g. Playwright's `devices['iPhone 13']` or an explicit small viewport) and
|
|
140
|
+
screenshot the section that changed. For interactive flows, enable video
|
|
141
|
+
recording, drive the interaction, then save the video. Save raw PNGs/videos to
|
|
142
|
+
`agents/<id>/screenshots/` ONLY (recordings under a `recordings/` subdir).
|
|
143
|
+
**NEVER `git add` a PNG** or a video — committing captured evidence is a
|
|
144
|
+
release-blocker bug.
|
|
106
145
|
3. Stop the dev server using the recorded stop command.
|
|
107
146
|
|
|
108
147
|
**Upload + embed — GitHub (release-asset CDN recipe).** GitHub has no
|
|
109
148
|
documented REST endpoint for PR-comment image uploads, so publish the PNGs as
|
|
110
149
|
assets on a dedicated per-PR prerelease and embed the asset CDN URLs. This works
|
|
111
|
-
on
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# Resolve each asset's public CDN url:
|
|
128
|
-
URL=$(GH_TOKEN=$GH_TOKEN gh api "repos/$OWNER/$REPO/releases/tags/$TAG" \
|
|
129
|
-
--jq '.assets[]|select(.name=="after.png")|.browser_download_url')
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
PowerShell:
|
|
133
|
-
|
|
134
|
-
```powershell
|
|
135
|
-
$OWNER='opg-microsoft'; $REPO='minions'; $PR=<num>; $TAG="pr-$PR-visual"
|
|
136
|
-
$env:GH_TOKEN = (gh auth token --user yemishin_microsoft --hostname github.com)
|
|
137
|
-
$pngs = Get-ChildItem agents/<id>/screenshots/*.png
|
|
138
|
-
gh release create $TAG $pngs --repo "$OWNER/$REPO" --title "PR #$PR visual evidence" --prerelease --notes "Visual evidence for PR #$PR"
|
|
139
|
-
if ($LASTEXITCODE -ne 0) { gh release upload $TAG $pngs --repo "$OWNER/$REPO" --clobber }
|
|
140
|
-
$URL = (gh api "repos/$OWNER/$REPO/releases/tags/$TAG" --jq '.assets[]|select(.name=="after.png")|.browser_download_url')
|
|
141
|
-
```
|
|
150
|
+
on private repositories and never commits PNGs to the source branch.
|
|
151
|
+
|
|
152
|
+
1. Resolve `<owner>/<repo>` from the current PR and GET-verify that exact slug.
|
|
153
|
+
2. Enumerate configured `gh` accounts without changing global auth. For each,
|
|
154
|
+
build a per-command `GH_TOKEN` and probe the exact slug; select only an
|
|
155
|
+
account whose repository response grants push permission. Fail closed when
|
|
156
|
+
none does. **NEVER run `gh auth switch` or hardcode an operator login.**
|
|
157
|
+
3. Set `TAG=pr-<num>-visual` and explicitly list this dispatch's PNG (and, for
|
|
158
|
+
interactive flows, video) files.
|
|
159
|
+
4. Query `gh release view "$TAG" --repo <owner>/<repo>` first. If the exact tag
|
|
160
|
+
exists, run `gh release upload "$TAG" <files> --clobber`; if it returns a
|
|
161
|
+
verified not-found response, run `gh release create`. Do not reinterpret an
|
|
162
|
+
arbitrary create failure as permission to overwrite assets.
|
|
163
|
+
5. Resolve each asset with `gh api
|
|
164
|
+
repos/<owner>/<repo>/releases/tags/$TAG --jq
|
|
165
|
+
'.assets[]|select(.name=="after.png")|.browser_download_url'`.
|
|
142
166
|
|
|
143
167
|
Then embed each asset in the PR body as `` (BEFORE/AFTER
|
|
144
168
|
pair for fixes, AFTER-only for new UI) and patch the description with
|
|
145
|
-
`gh pr edit <num> --repo <owner>/<repo> --body-file <file>`.
|
|
169
|
+
`gh pr edit <num> --repo <owner>/<repo> --body-file <file>`. Recordings upload as
|
|
170
|
+
assets on the same release; embed them as a plain markdown link (video files do
|
|
171
|
+
not render inline) — `[<view> (recording)](<url>)`.
|
|
146
172
|
|
|
147
173
|
**Upload + embed — Azure DevOps.** ADO exposes a scriptable attachment API:
|
|
148
|
-
`PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes,
|
|
149
|
-
reference the returned URL in the description. This now applies to
|
|
150
|
-
newly-introduced screenshots too, not just refreshes.
|
|
174
|
+
`PUT /pullRequests/<id>/attachments/<filename>` with the PNG (or video) bytes,
|
|
175
|
+
then reference the returned URL in the description. This now applies to
|
|
176
|
+
newly-introduced screenshots and recordings too, not just refreshes.
|
|
151
177
|
|
|
152
178
|
**Degrade gracefully.** If the dev server won't start, the route 404s,
|
|
153
179
|
Playwright is unavailable, or the upload fails → **SKIP** without failing the
|
|
154
180
|
work item and record the skip + reason in `meta.descriptionAudit` (e.g.
|
|
155
|
-
`result: "screenshots-skipped (dev-server-unavailable)"`). Screenshots
|
|
156
|
-
land in the repo — `agents/<id>/screenshots/` only
|
|
181
|
+
`result: "screenshots-skipped (dev-server-unavailable)"`). Screenshots and
|
|
182
|
+
recordings NEVER land in the repo — `agents/<id>/screenshots/` only (videos under
|
|
183
|
+
a `recordings/` subdir); never `git add` a PNG or a video.
|
|
157
184
|
|
|
158
185
|
## Out-of-scope guardrails
|
|
159
186
|
|
|
160
|
-
- Only
|
|
161
|
-
skip backend/engine/text/analysis/docs changes — they add
|
|
187
|
+
- Only capture visual evidence for **meaningful visual/UI changes** (see the
|
|
188
|
+
scope guard above); skip backend/engine/text/analysis/docs changes — they add
|
|
189
|
+
no signal.
|
|
162
190
|
- Don't rewrite description prose beyond the targeted stale edits.
|
|
163
191
|
- Don't modify the PR title.
|
|
164
192
|
- Don't toggle draft state, close/reopen the PR, or post a separate PR
|
|
165
193
|
comment narrating the description change.
|
|
166
|
-
- Don't fail the work item on a screenshot or upload/attachment
|
|
167
|
-
degrade gracefully and record the skip.
|
|
168
|
-
- Screenshots **never** land in the repo. Save raw PNGs only under
|
|
169
|
-
`agents/<id>/screenshots
|
|
194
|
+
- Don't fail the work item on a screenshot, recording, or upload/attachment
|
|
195
|
+
failure — degrade gracefully and record the skip.
|
|
196
|
+
- Screenshots and recordings **never** land in the repo. Save raw PNGs only under
|
|
197
|
+
`agents/<id>/screenshots/` (videos under a `recordings/` subdir). No `git add`
|
|
198
|
+
of any PNG or video, ever.
|
|
170
199
|
- Never run `gh auth switch`; build a per-command `GH_TOKEN` for the repo
|
|
171
200
|
owner's account.
|
|
172
201
|
|
|
@@ -193,9 +222,12 @@ this block in the JSON completion report (see
|
|
|
193
222
|
When you **introduce** screenshots for a visual change, set
|
|
194
223
|
`result: "screenshots-introduced"` (or
|
|
195
224
|
`"description-patched+screenshots-introduced"` when you also patched prose),
|
|
196
|
-
and list the embedded asset URLs in `newScreenshots`.
|
|
197
|
-
|
|
198
|
-
`"screenshots-
|
|
225
|
+
and list the embedded asset URLs in `newScreenshots`. When you also (or only)
|
|
226
|
+
attach a **screen recording**, use `result: "recordings-introduced"` (or combine,
|
|
227
|
+
e.g. `"screenshots-introduced+recordings-introduced"`) and list the video asset
|
|
228
|
+
URLs in `newScreenshots` alongside the images. Use `"screenshots-refreshed"` when
|
|
229
|
+
you only swapped pre-existing image refs, and a `"screenshots-skipped (<reason>)"`
|
|
230
|
+
shape when capture/upload was skipped.
|
|
199
231
|
|
|
200
232
|
Omit `meta.descriptionAudit` entirely when the audit did not run (no
|
|
201
233
|
commits pushed, no-op completion, etc.). When `meta.skipDescriptionAudit`
|
|
@@ -78,6 +78,8 @@ Use subagents only for genuinely parallel, independent tasks (e.g., editing file
|
|
|
78
78
|
|
|
79
79
|
Before starting work, run `git status` and verify the worktree is clean and on the expected branch (`{{pr_branch}}`). If the worktree is dirty or on the wrong branch, report the issue and stop.
|
|
80
80
|
|
|
81
|
+
An untracked root-level `.minions-worktree` entry is expected Minions orchestration metadata, not source dirt. Do not delete or modify the marker. Any other status entry, including staged, modified, deleted, or other untracked files, means the worktree is dirty.
|
|
82
|
+
|
|
81
83
|
## Validation
|
|
82
84
|
|
|
83
85
|
Before pushing, prove the fix did not introduce regressions:
|