@mmerterden/multi-agent-pipeline 13.3.0 → 13.5.0
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/CHANGELOG.md +131 -0
- package/install/_mcp-register.mjs +125 -0
- package/install/codex.mjs +4 -38
- package/install/copilot.mjs +8 -0
- package/install/index.mjs +8 -3
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -1
- package/pipeline/lib/account-resolver.sh +15 -4
- package/pipeline/lib/credential-inventory.sh +374 -0
- package/pipeline/lib/credential-store-resolver.sh +34 -5
- package/pipeline/lib/fetch-confluence.sh +27 -5
- package/pipeline/lib/fetch-crashlytics.sh +27 -5
- package/pipeline/lib/fetch-figma-annotations.sh +18 -4
- package/pipeline/lib/fetch-fortify.sh +27 -5
- package/pipeline/lib/fetch-graylog.sh +27 -5
- package/pipeline/lib/figma-mcp-refresh.sh +35 -11
- package/pipeline/lib/figma-screenshot.sh +18 -7
- package/pipeline/lib/issue-fetcher.sh +19 -4
- package/pipeline/lib/post-pr-review.sh +27 -4
- package/pipeline/lib/repo-cache.sh +19 -4
- package/pipeline/lib/vercel-deploy.sh +15 -4
- package/pipeline/multi-agent-refs/features/external-context-injection.md +44 -3
- package/pipeline/multi-agent-refs/keychain.md +63 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +51 -18
- package/pipeline/multi-agent-refs/rules.md +3 -3
- package/pipeline/scripts/audit-log.sh +10 -4
- package/pipeline/scripts/keychain-save.sh +27 -5
- package/pipeline/scripts/phase-tracker.sh +26 -2
- package/pipeline/scripts/phase0-exit-gate.mjs +52 -0
- package/pipeline/scripts/uninstall.mjs +24 -5
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +19 -0
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
* and spacing guessed at 16 where the frame said `Spacing/12`. Half the commits on
|
|
15
15
|
* that branch were rework.
|
|
16
16
|
*
|
|
17
|
+
* A second run then showed the other half of the same failure: a Jira task was
|
|
18
|
+
* implemented without the repo or branch picker ever appearing, straight on whatever
|
|
19
|
+
* the local checkout was pointing at. Step 3 and Step 8 own those decisions, and
|
|
20
|
+
* `agent-state.json` is the only durable evidence they happened - so the gate asserts
|
|
21
|
+
* their output too, not just `taskType`.
|
|
22
|
+
*
|
|
17
23
|
* A phase that reports success without its output is worse than one that fails:
|
|
18
24
|
* every later phase then reasons from a field that is not there. So this is a
|
|
19
25
|
* gate, not a lint - the spec already said what to write, and prose alone did
|
|
@@ -120,6 +126,52 @@ export function evaluate(state, extraInput = "") {
|
|
|
120
126
|
);
|
|
121
127
|
}
|
|
122
128
|
|
|
129
|
+
// Step 3 picks the base branch and Step 8 creates the worktree. A run that skipped
|
|
130
|
+
// both develops on whatever the checkout happened to have, which is how a Jira task
|
|
131
|
+
// got implemented on a stale local branch with no picker ever shown. The state file
|
|
132
|
+
// is the only durable evidence those steps ran, so it has to carry their output.
|
|
133
|
+
//
|
|
134
|
+
// `local` provider runs still choose a base branch; what they skip is push and PR.
|
|
135
|
+
// So this applies to every provider.
|
|
136
|
+
const baseBranch = typeof state.baseBranch === "string" ? state.baseBranch.trim() : "";
|
|
137
|
+
if (!baseBranch) {
|
|
138
|
+
failures.push(
|
|
139
|
+
"agent-state.json has no baseBranch. Step 3 asks for it (or takes it from the " +
|
|
140
|
+
"input); an unset value means the branch picker never ran and development " +
|
|
141
|
+
"happens on whatever the checkout was already pointing at.",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Which ref the base actually came from. Without it, "built on a fresh origin ref"
|
|
146
|
+
// and "built on a month-old local branch" are indistinguishable after the fact -
|
|
147
|
+
// and the fetch-failure picker exists precisely to make that an explicit choice.
|
|
148
|
+
const fetchStatus = typeof state.baseFetchStatus === "string" ? state.baseFetchStatus.trim() : "";
|
|
149
|
+
const FETCH_STATES = ["fresh", "cached-stale", "local-branch", "aborted"];
|
|
150
|
+
if (baseBranch && !FETCH_STATES.includes(fetchStatus)) {
|
|
151
|
+
failures.push(
|
|
152
|
+
`agent-state.json has baseFetchStatus="${fetchStatus || "<unset>"}"; Step 3 must ` +
|
|
153
|
+
`record one of ${FETCH_STATES.join(" | ")}. An unset value means nothing ` +
|
|
154
|
+
`established whether the base ref was fetched or reused from a stale cache.`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Worktree isolation is a standing rule: never develop in the primary checkout.
|
|
159
|
+
const worktree = typeof state.worktreePath === "string" ? state.worktreePath.trim() : "";
|
|
160
|
+
const projectRoot = typeof state.projectRoot === "string" ? state.projectRoot.trim() : "";
|
|
161
|
+
if (!worktree && !projectRoot) {
|
|
162
|
+
failures.push(
|
|
163
|
+
"agent-state.json records neither worktreePath nor projectRoot. Step 8 creates " +
|
|
164
|
+
"the worktree; with no recorded path, later phases cannot tell which tree they " +
|
|
165
|
+
"are meant to edit and default to the current directory.",
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (worktree && projectRoot && worktree === projectRoot) {
|
|
169
|
+
failures.push(
|
|
170
|
+
`worktreePath equals projectRoot (${worktree}). Development must happen in a ` +
|
|
171
|
+
`separate worktree, never in the primary checkout.`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
123
175
|
// The Figma access chain records which tier answered. A component task with no
|
|
124
176
|
// recorded tier means nothing verified that the design was actually reachable,
|
|
125
177
|
// which is how a run ends up guessing spacing.
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
39
39
|
import { execFileSync } from "child_process";
|
|
40
40
|
import { join } from "path";
|
|
41
|
+
import { homedir } from "os";
|
|
41
42
|
import { pathToFileURL } from "url";
|
|
42
43
|
import { createInterface } from "readline";
|
|
43
44
|
|
|
@@ -157,21 +158,38 @@ function removeGeneratedCodexAgents(agentsDir) {
|
|
|
157
158
|
* uninstall failure. Never edits `config.toml` directly - Codex keeps
|
|
158
159
|
* marketplace and plugin state in the same file.
|
|
159
160
|
*/
|
|
160
|
-
function
|
|
161
|
+
function deregisterMcpServer(host, cliPath) {
|
|
162
|
+
const cli = cliPath || host;
|
|
161
163
|
if (dryRun) {
|
|
162
|
-
report("would run",
|
|
164
|
+
report("would run", `${host} mcp remove dev-toolkit`);
|
|
163
165
|
return;
|
|
164
166
|
}
|
|
165
167
|
try {
|
|
166
|
-
execFileSync(
|
|
167
|
-
console.log(
|
|
168
|
+
execFileSync(cli, ["mcp", "remove", "dev-toolkit"], { stdio: "pipe", timeout: 20_000 });
|
|
169
|
+
console.log(` removed: dev-toolkit MCP registration (${host})`);
|
|
168
170
|
} catch {
|
|
169
171
|
console.log(
|
|
170
|
-
|
|
172
|
+
` note: could not run \`${host} mcp remove dev-toolkit\` (${host} not on PATH, or not registered)`,
|
|
171
173
|
);
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
function deregisterCodexMcpServer() {
|
|
178
|
+
deregisterMcpServer("codex");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Copilot registers the same server, so leaving it behind would keep a stale entry
|
|
183
|
+
* pointing at a package the user just removed.
|
|
184
|
+
*
|
|
185
|
+
* `copilot` is often a shell alias for `gh copilot`, invisible to execFileSync, so the
|
|
186
|
+
* real binary path is tried when the bare name is not resolvable.
|
|
187
|
+
*/
|
|
188
|
+
function deregisterCopilotMcpServer() {
|
|
189
|
+
const ghCopilot = join(homedir(), ".local", "share", "gh", "copilot", "copilot");
|
|
190
|
+
deregisterMcpServer("copilot", existsSync(ghCopilot) ? ghCopilot : undefined);
|
|
191
|
+
}
|
|
192
|
+
|
|
175
193
|
/**
|
|
176
194
|
* Remove every directory under `parent` whose name matches a predicate.
|
|
177
195
|
* @param {string} parent
|
|
@@ -475,6 +493,7 @@ export async function main() {
|
|
|
475
493
|
if (forCopilot && HOME) {
|
|
476
494
|
console.log("");
|
|
477
495
|
console.log(" [Copilot CLI] Removing...");
|
|
496
|
+
deregisterCopilotMcpServer();
|
|
478
497
|
const COP = join(HOME, ".copilot");
|
|
479
498
|
rmIfExists(join(COP, "scripts"));
|
|
480
499
|
// Pipeline-managed trees the installer lays down alongside scripts/.
|
|
@@ -336,6 +336,25 @@ Full contract: `refs/tracker-contract.md` section "TaskCreate ordering (strict)"
|
|
|
336
336
|
> 6. **Branch name confirm** - `feature/PROJ-{id}-{kebab}` or `bugfix/...` - user confirms or edits. In multi-repo mode the branch name is shared across all selected repos (collision check runs per-repo; any collision applies the suffix to all). (Step 4)
|
|
337
337
|
> 7. **Git identity - per repo** - route via `prefs.global.platformIdentityRouting`; ask if ambiguous. Multi-repo mode resolves identity **per repo independently** (phase-0-init.md L283). (Step 6)
|
|
338
338
|
> 8. **Instruction files + workspace creation - serially per repo** - detect `.instructions/figma/` etc. In multi-repo mode, worktree creation loops per repo serially (phase-0-init.md L308); any failure rolls back previously-created worktrees. Write `agent-state.json` with `state.projects[]` array (scalar `project`/`projectRoot`/`branch` mirror `projects[0]` for back-compat). (Steps 7-8)
|
|
339
|
+
>
|
|
340
|
+
> **Exit gate (BLOCKING).** The contract above was prose only, and a run still took a
|
|
341
|
+
> Jira ID and implemented straight onto the local checkout with no picker shown. Before
|
|
342
|
+
> marking Phase 0 completed:
|
|
343
|
+
>
|
|
344
|
+
> ```bash
|
|
345
|
+
> node "$HOME/.copilot/scripts/phase0-exit-gate.mjs" "$TASK_ID" --input "$USER_INPUT"
|
|
346
|
+
> ```
|
|
347
|
+
>
|
|
348
|
+
> Non-zero means Phase 0 did not produce its own output - `taskType`, `baseBranch`,
|
|
349
|
+
> `baseFetchStatus`, a worktree path distinct from the project root, and a recorded Figma
|
|
350
|
+
> access tier when the input carries a Figma URL. Fix the missing step; do not proceed.
|
|
351
|
+
>
|
|
352
|
+
> **Credential inventory (Step 0).** Run
|
|
353
|
+
> `bash "$HOME/.copilot/lib/credential-inventory.sh" --json` and store the result in
|
|
354
|
+
> `agent-state.json.credentialInventory`. Per `refs/keychain.md` Rule 2, never ask the
|
|
355
|
+
> user for data a mapped credential can fetch: a run asked for a hand-pasted Crashlytics
|
|
356
|
+
> stack trace while a valid Firebase service account was mapped and resolving. Ask for
|
|
357
|
+
> the pointer (the issue URL) instead, and say what you will do with it.
|
|
339
358
|
|
|
340
359
|
**Step 1 - Project detection + input parsing:**
|
|
341
360
|
|