@mmerterden/multi-agent-pipeline 17.6.0 → 18.0.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 +127 -0
- package/README.md +43 -1
- package/README.tr.md +41 -0
- package/docs/adr/0011-dormant-ci.md +25 -1
- package/docs/server-readiness.md +188 -0
- package/index.js +16 -1
- package/install/_common.mjs +42 -17
- package/install/_dev-only-files.mjs +8 -0
- package/install/_unattended-profile.mjs +113 -0
- package/install/index.mjs +48 -0
- package/manifest.json +1049 -0
- package/package.json +5 -2
- package/pipeline/commands/multi-agent/status/SKILL.md +52 -21
- package/pipeline/lib/_jira-auth.sh +8 -0
- package/pipeline/lib/analysis-jira-write.sh +32 -0
- package/pipeline/lib/ask-choice.sh +13 -2
- package/pipeline/lib/autopilot-state.sh +8 -0
- package/pipeline/lib/fatal.mjs +129 -0
- package/pipeline/lib/figma-mcp-refresh.sh +18 -0
- package/pipeline/lib/figma-screenshot.sh +18 -0
- package/pipeline/lib/invoked-directly.mjs +43 -0
- package/pipeline/lib/jira-publish.sh +42 -0
- package/pipeline/lib/md2confluence-v3.py +47 -0
- package/pipeline/lib/outbound-gate.mjs +175 -0
- package/pipeline/lib/plan-todos.sh +27 -6
- package/pipeline/lib/post-pr-review.sh +77 -8
- package/pipeline/lib/repo-hygiene.sh +8 -3
- package/pipeline/lib/require-jq.sh +40 -0
- package/pipeline/lib/run-paths.sh +335 -0
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +70 -0
- package/pipeline/multi-agent-refs/features/cost-analysis.md +93 -0
- package/pipeline/multi-agent-refs/features/doctor.md +45 -0
- package/pipeline/multi-agent-refs/features/verify.md +83 -0
- package/pipeline/multi-agent-refs/phases/operations.md +13 -2
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -1
- package/pipeline/multi-agent-refs/unattended-contract.md +129 -0
- package/pipeline/scripts/_run-paths.mjs +372 -0
- package/pipeline/scripts/aggregate-metrics.mjs +64 -64
- package/pipeline/scripts/autopilot-arming.mjs +2 -1
- package/pipeline/scripts/autopilot-intake.mjs +2 -1
- package/pipeline/scripts/autopilot-runner.mjs +206 -2
- package/pipeline/scripts/build-references.mjs +2 -1
- package/pipeline/scripts/build-stack-plugins.mjs +10 -2
- package/pipeline/scripts/capture-evidence.sh +7 -2
- package/pipeline/scripts/classify-plan-safety.mjs +2 -1
- package/pipeline/scripts/cost-analyze.mjs +600 -0
- package/pipeline/scripts/cost-budget-check.mjs +4 -12
- package/pipeline/scripts/council-view.mjs +2 -1
- package/pipeline/scripts/crush-json.mjs +2 -1
- package/pipeline/scripts/diff-explain.mjs +6 -9
- package/pipeline/scripts/diff-risk-score.mjs +2 -1
- package/pipeline/scripts/doctor.mjs +138 -4
- package/pipeline/scripts/evidence-gate.mjs +9 -3
- package/pipeline/scripts/feedback-send.mjs +12 -2
- package/pipeline/scripts/gc-abandoned.sh +29 -13
- package/pipeline/scripts/gc-worktrees.sh +11 -4
- package/pipeline/scripts/github-ssh-setup.sh +64 -7
- package/pipeline/scripts/graph-mermaid.mjs +4 -2
- package/pipeline/scripts/keychain-save.sh +101 -30
- package/pipeline/scripts/learn-from-transcripts.mjs +2 -1
- package/pipeline/scripts/learning-curve.mjs +34 -29
- package/pipeline/scripts/make-manifest.mjs +199 -0
- package/pipeline/scripts/migrate-prefs.mjs +2 -1
- package/pipeline/scripts/migrate-state.mjs +94 -4
- package/pipeline/scripts/phase-banner.sh +6 -2
- package/pipeline/scripts/phase-tracker.sh +41 -3
- package/pipeline/scripts/plan-coverage-gate.mjs +6 -2
- package/pipeline/scripts/pre-commit-check.sh +7 -0
- package/pipeline/scripts/pre-push-check.sh +7 -0
- package/pipeline/scripts/purge.sh +23 -6
- package/pipeline/scripts/render-agent-log-cost.sh +9 -2
- package/pipeline/scripts/render-cost-summary.sh +9 -2
- package/pipeline/scripts/render-work-summary.sh +11 -4
- package/pipeline/scripts/review-file-filter.mjs +4 -2
- package/pipeline/scripts/review-scope.mjs +2 -1
- package/pipeline/scripts/routine-registry.mjs +2 -1
- package/pipeline/scripts/run-aggregator.mjs +13 -14
- package/pipeline/scripts/run-metrics.mjs +3 -1
- package/pipeline/scripts/runs-index.mjs +343 -0
- package/pipeline/scripts/scorecard-snapshot.mjs +178 -0
- package/pipeline/scripts/search-logs.sh +18 -0
- package/pipeline/scripts/test-gap-scan.mjs +2 -1
- package/pipeline/scripts/test-integrity-gate.mjs +2 -1
- package/pipeline/scripts/update-issue-progress.sh +56 -7
- package/pipeline/scripts/usage-report.mjs +12 -1
- package/pipeline/scripts/validate-analysis-doc.mjs +2 -1
- package/pipeline/scripts/validate-code-graph.mjs +6 -3
- package/pipeline/scripts/validate-complaint-doc.mjs +2 -1
- package/pipeline/scripts/validate-diff-risk.mjs +6 -3
- package/pipeline/scripts/validate-test-gap.mjs +6 -3
- package/pipeline/scripts/validate-triage.mjs +3 -1
- package/pipeline/scripts/verify-citations.mjs +4 -2
- package/pipeline/scripts/verify.mjs +327 -0
- package/pipeline/scripts/worktree-finalize.sh +13 -4
- package/pipeline/scripts/write-state.mjs +154 -15
- package/pipeline/skills/.skill-manifest.json +2 -2
- package/pipeline/skills/.skills-index.json +56 -1
- package/pipeline/skills/shared/README.md +8 -3
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +33 -9
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/package_app.sh +4 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +4 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +2 -1
- package/pipeline/skills/skills-index.md +6 -1
|
@@ -16,12 +16,22 @@
|
|
|
16
16
|
*
|
|
17
17
|
* # Replace: full overwrite from stdin JSON
|
|
18
18
|
* cat new-state.json | node write-state.mjs --replace /path/to/agent-state.json
|
|
19
|
+
* cat patch.json | node write-state.mjs --if-rev=7 /path/to/agent-state.json
|
|
20
|
+
*
|
|
21
|
+
* `--if-rev=<n>` is compare-and-swap: write only if the record on disk is
|
|
22
|
+
* still at revision n. The lock closes the window between two WRITERS; it does
|
|
23
|
+
* nothing about the one between a read and a write, which is where an editor
|
|
24
|
+
* lives - `/multi-agent:steer` reads the state, a human thinks, and the run may
|
|
25
|
+
* move three phases before the edit lands. Without this the edit merges over
|
|
26
|
+
* the top and reinstates decisions the run already replaced.
|
|
19
27
|
*
|
|
20
28
|
* Exit codes:
|
|
21
29
|
* 0 - write succeeded
|
|
22
30
|
* 1 - invalid JSON on stdin
|
|
23
31
|
* 2 - lock timeout (another writer held the lock past the acquire window)
|
|
32
|
+
* 4 - the lock was taken from us mid-write; nothing was written, retry
|
|
24
33
|
* 3 - I/O error (disk full, permission denied, parent dir missing)
|
|
34
|
+
* 5 - --if-rev did not match; the record moved, nothing was written
|
|
25
35
|
*
|
|
26
36
|
* Tunables (env):
|
|
27
37
|
* WRITE_STATE_LOCK_TIMEOUT_MS acquire window before exit 2 (default 15000)
|
|
@@ -52,9 +62,11 @@ import {
|
|
|
52
62
|
} from "fs";
|
|
53
63
|
import { dirname, basename } from "path";
|
|
54
64
|
import { Buffer } from "node:buffer";
|
|
65
|
+
import { runMain } from "../lib/fatal.mjs";
|
|
66
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
55
67
|
|
|
56
68
|
/**
|
|
57
|
-
* @typedef {{ replace: boolean, path: string }} ParsedArgs
|
|
69
|
+
* @typedef {{ replace: boolean, path: string, ifRev: number|null }} ParsedArgs
|
|
58
70
|
*/
|
|
59
71
|
|
|
60
72
|
/** @returns {ParsedArgs} */
|
|
@@ -62,18 +74,26 @@ function parseArgs() {
|
|
|
62
74
|
const args = process.argv.slice(2);
|
|
63
75
|
let replace = false;
|
|
64
76
|
let path = null;
|
|
77
|
+
let ifRev = null;
|
|
65
78
|
for (const a of args) {
|
|
66
79
|
if (a === "--replace") replace = true;
|
|
67
|
-
else if (a.startsWith("--")) {
|
|
80
|
+
else if (a.startsWith("--if-rev=")) {
|
|
81
|
+
const v = Number(a.slice("--if-rev=".length));
|
|
82
|
+
if (!Number.isInteger(v) || v < 0) {
|
|
83
|
+
console.error(`write-state: --if-rev takes a non-negative integer, got ${a.slice(9)}`);
|
|
84
|
+
process.exit(3);
|
|
85
|
+
}
|
|
86
|
+
ifRev = v;
|
|
87
|
+
} else if (a.startsWith("--")) {
|
|
68
88
|
console.error(`write-state: unknown flag ${a}`);
|
|
69
89
|
process.exit(3);
|
|
70
90
|
} else path = a;
|
|
71
91
|
}
|
|
72
92
|
if (!path) {
|
|
73
|
-
console.error("usage: write-state.mjs [--replace] <path>");
|
|
93
|
+
console.error("usage: write-state.mjs [--replace] [--if-rev=<n>] <path>");
|
|
74
94
|
process.exit(3);
|
|
75
95
|
}
|
|
76
|
-
return { replace, path };
|
|
96
|
+
return { replace, path, ifRev };
|
|
77
97
|
}
|
|
78
98
|
|
|
79
99
|
/**
|
|
@@ -103,8 +123,13 @@ async function acquireLock(
|
|
|
103
123
|
try {
|
|
104
124
|
writeFileSync(stagePath, String(process.pid));
|
|
105
125
|
linkSync(stagePath, lockPath); // fails EEXIST if another writer holds it
|
|
126
|
+
// The inode of the lock WE created. Everything after this point checks
|
|
127
|
+
// against it: a lock at the same path with a different inode is somebody
|
|
128
|
+
// else's, and both releasing it and writing under it are the same bug the
|
|
129
|
+
// reclaim path below already refuses to commit.
|
|
130
|
+
const ino = statSync(lockPath).ino;
|
|
106
131
|
unlinkSync(stagePath);
|
|
107
|
-
return;
|
|
132
|
+
return ino;
|
|
108
133
|
} catch (err) {
|
|
109
134
|
try {
|
|
110
135
|
unlinkSync(stagePath);
|
|
@@ -208,16 +233,46 @@ function lockIdentityIfStale(lockPath, staleMs) {
|
|
|
208
233
|
// Path whose lock this process currently holds (null when none). Lets the
|
|
209
234
|
// top-level error handler release our lock on unexpected failures.
|
|
210
235
|
let heldLockFor = null;
|
|
236
|
+
// The inode of that lock. The path alone does not identify it: a lock deleted
|
|
237
|
+
// and re-created at the same path is a different file and a different owner.
|
|
238
|
+
let heldLockIno = null;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* True when the lock at `path` is still the one this process created.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} path
|
|
244
|
+
* @param {number | null} ino
|
|
245
|
+
* @returns {boolean}
|
|
246
|
+
*/
|
|
247
|
+
function stillOurs(path, ino) {
|
|
248
|
+
if (ino === null) return true; // nothing was ever locked; nothing to lose
|
|
249
|
+
try {
|
|
250
|
+
return statSync(`${path}.lock`).ino === ino;
|
|
251
|
+
} catch {
|
|
252
|
+
return false; // our lock is gone, which means somebody removed it
|
|
253
|
+
}
|
|
254
|
+
}
|
|
211
255
|
|
|
212
256
|
/** @param {string} path */
|
|
213
|
-
function releaseLock(path) {
|
|
257
|
+
function releaseLock(path, ino = heldLockIno) {
|
|
214
258
|
const lockPath = `${path}.lock`;
|
|
215
259
|
try {
|
|
260
|
+
// By identity, never by path - the same rule acquireLock states and this
|
|
261
|
+
// function used to break. If our lock was reclaimed as stale while we were
|
|
262
|
+
// starved (the abandon ceiling, or the unreadable-PID window), the lock now
|
|
263
|
+
// at this path belongs to a LIVE writer, and unlinking it puts two writers
|
|
264
|
+
// inside at once and loses one update. That is the third point in this
|
|
265
|
+
// cycle where "delete by path" produces the same data loss; the other two
|
|
266
|
+
// are documented in acquireLock.
|
|
267
|
+
if (ino !== null && statSync(lockPath).ino !== ino) return;
|
|
216
268
|
unlinkSync(lockPath);
|
|
217
269
|
} catch {
|
|
218
270
|
/* already gone - fine */
|
|
219
271
|
}
|
|
220
|
-
if (heldLockFor === path)
|
|
272
|
+
if (heldLockFor === path) {
|
|
273
|
+
heldLockFor = null;
|
|
274
|
+
heldLockIno = null;
|
|
275
|
+
}
|
|
221
276
|
}
|
|
222
277
|
|
|
223
278
|
/**
|
|
@@ -225,6 +280,22 @@ function releaseLock(path) {
|
|
|
225
280
|
* @param {object} patch
|
|
226
281
|
* @returns {object}
|
|
227
282
|
*/
|
|
283
|
+
/**
|
|
284
|
+
* The version a NEWLY created state document is stamped with, read from the
|
|
285
|
+
* schema so the writer and the schema cannot disagree. The literal is a
|
|
286
|
+
* fallback for an unreadable schema, which validate-schemas.mjs would already
|
|
287
|
+
* have failed on.
|
|
288
|
+
*/
|
|
289
|
+
const STATE_SCHEMA_VERSION = (() => {
|
|
290
|
+
try {
|
|
291
|
+
const schemaPath = new URL("../schemas/agent-state.schema.json", import.meta.url);
|
|
292
|
+
const sv = JSON.parse(readFileSync(schemaPath, "utf-8"))?.properties?.schemaVersion;
|
|
293
|
+
return sv?.const ?? sv?.enum?.at(-1) ?? "2.1.0";
|
|
294
|
+
} catch {
|
|
295
|
+
return "2.1.0";
|
|
296
|
+
}
|
|
297
|
+
})();
|
|
298
|
+
|
|
228
299
|
function deepMerge(base, patch) {
|
|
229
300
|
if (Array.isArray(patch)) return patch; // arrays replace
|
|
230
301
|
if (typeof patch !== "object" || patch === null) return patch;
|
|
@@ -236,7 +307,7 @@ function deepMerge(base, patch) {
|
|
|
236
307
|
}
|
|
237
308
|
|
|
238
309
|
async function main() {
|
|
239
|
-
const { replace, path } = parseArgs();
|
|
310
|
+
const { replace, path, ifRev } = parseArgs();
|
|
240
311
|
|
|
241
312
|
// Read stdin
|
|
242
313
|
const chunks = [];
|
|
@@ -257,7 +328,7 @@ async function main() {
|
|
|
257
328
|
}
|
|
258
329
|
|
|
259
330
|
try {
|
|
260
|
-
await acquireLock(path);
|
|
331
|
+
heldLockIno = await acquireLock(path);
|
|
261
332
|
heldLockFor = path;
|
|
262
333
|
} catch (err) {
|
|
263
334
|
if (err.code === "LOCK_TIMEOUT") {
|
|
@@ -272,6 +343,8 @@ async function main() {
|
|
|
272
343
|
// here must release OUR lock explicitly before exiting - otherwise a corrupt
|
|
273
344
|
// state file (or any I/O error) leaks agent-state.json.lock and blocks the
|
|
274
345
|
// next writer until the stale-lock window elapses.
|
|
346
|
+
const fileExistedBefore = existsSync(path);
|
|
347
|
+
|
|
275
348
|
try {
|
|
276
349
|
let next;
|
|
277
350
|
if (replace) {
|
|
@@ -304,16 +377,67 @@ async function main() {
|
|
|
304
377
|
// carries a `rev` from before the record moved, and following it down
|
|
305
378
|
// would tell the next reader that nothing changed.
|
|
306
379
|
let priorRev = Number.isInteger(next.rev) ? next.rev : 0;
|
|
380
|
+
let diskRev = null;
|
|
307
381
|
if (existsSync(path)) {
|
|
308
382
|
try {
|
|
309
383
|
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
310
|
-
if (Number.isInteger(parsed?.rev))
|
|
384
|
+
if (Number.isInteger(parsed?.rev)) {
|
|
385
|
+
diskRev = parsed.rev;
|
|
386
|
+
priorRev = Math.max(priorRev, parsed.rev);
|
|
387
|
+
}
|
|
311
388
|
} catch {
|
|
312
389
|
/* unreadable on disk - the merge path above already threw for that */
|
|
313
390
|
}
|
|
314
391
|
}
|
|
392
|
+
|
|
393
|
+
// Compare-and-swap. `rev` already told a reader whether the record moved;
|
|
394
|
+
// --if-rev is what lets it ACT on that instead of merging over the top.
|
|
395
|
+
//
|
|
396
|
+
// The lock closes the window between two writers. It does not close the
|
|
397
|
+
// one between a READ and a write, and `/multi-agent:steer` puts a second
|
|
398
|
+
// writer in exactly that window on purpose: it reads the state, a human
|
|
399
|
+
// thinks, and by the time the edit lands the run may have moved three
|
|
400
|
+
// phases. A merge then reinstates decisions the run already replaced.
|
|
401
|
+
//
|
|
402
|
+
// Refusing costs a re-read. Merging costs a phase that silently reverts.
|
|
403
|
+
if (ifRev !== null) {
|
|
404
|
+
const seen = diskRev ?? 0;
|
|
405
|
+
if (seen !== ifRev) {
|
|
406
|
+
console.error(
|
|
407
|
+
`write-state: rev is ${seen}, expected ${ifRev} - the record moved since you read it; re-read and retry`,
|
|
408
|
+
);
|
|
409
|
+
releaseLock(path);
|
|
410
|
+
process.exit(5);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
315
414
|
next.rev = priorRev + 1;
|
|
316
415
|
|
|
416
|
+
// schemaVersion is stamped only when this call CREATES the document. The
|
|
417
|
+
// migration runner keys on it, and a document that never carries one can
|
|
418
|
+
// never be migrated - measured on a real install, 43 of 43 state files had
|
|
419
|
+
// no schemaVersion, so pipeline/schemas/migrations/ was unreachable code.
|
|
420
|
+
// A merge into an existing unstamped file is deliberately left alone:
|
|
421
|
+
// stamping there would claim a conformance nothing verified.
|
|
422
|
+
if (!fileExistedBefore && typeof next.schemaVersion !== "string") {
|
|
423
|
+
next.schemaVersion = STATE_SCHEMA_VERSION;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Last check before the write lands: is the lock still ours? Everything
|
|
427
|
+
// above read the file under a lock we believed we held, and if that belief
|
|
428
|
+
// stopped being true the rename would overwrite a concurrent writer's
|
|
429
|
+
// update with a merge that never saw it. Reporting success while losing an
|
|
430
|
+
// update is the one outcome this script exists to prevent, so it refuses
|
|
431
|
+
// instead - a caller that sees exit 4 can read and retry, which a caller
|
|
432
|
+
// that saw exit 0 never will.
|
|
433
|
+
if (!stillOurs(path, heldLockIno)) {
|
|
434
|
+
console.error(
|
|
435
|
+
`write-state: the lock was taken from us mid-write (${path}.lock). ` +
|
|
436
|
+
`Nothing was written. Re-read the state and retry.`,
|
|
437
|
+
);
|
|
438
|
+
process.exit(4);
|
|
439
|
+
}
|
|
440
|
+
|
|
317
441
|
const tmp = `${dirname(path)}/.${basename(path)}.${process.pid}.tmp`;
|
|
318
442
|
writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
|
|
319
443
|
renameSync(tmp, path); // atomic on POSIX
|
|
@@ -327,8 +451,23 @@ async function main() {
|
|
|
327
451
|
process.exit(0);
|
|
328
452
|
}
|
|
329
453
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
454
|
+
// Only run when invoked as a program. The lock helpers are the part with the
|
|
455
|
+
// subtle behaviour and they are unreachable from a test otherwise: the window
|
|
456
|
+
// they guard is microseconds wide, so a test that drives this file as a CLI
|
|
457
|
+
// measures the scheduler rather than the code.
|
|
458
|
+
const isDirectRun = invokedDirectly(import.meta.url);
|
|
459
|
+
if (isDirectRun) {
|
|
460
|
+
// Exit 3 is this file's documented "died without writing" code, and the
|
|
461
|
+
// cleanup is the reason the handler exists at all: a writer that dies holding
|
|
462
|
+
// the advisory lock makes the NEXT writer wait out the acquire window and
|
|
463
|
+
// then decide, on nothing but age, whether to take a lock that may still be
|
|
464
|
+
// live. Releasing on the abnormal path keeps that decision from arising.
|
|
465
|
+
runMain("write-state", main, {
|
|
466
|
+
code: 3,
|
|
467
|
+
cleanup: () => {
|
|
468
|
+
if (heldLockFor) releaseLock(heldLockFor);
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export { acquireLock, releaseLock, stillOurs, lockIdentityIfStale };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-17T11:16:58Z",
|
|
4
4
|
"skillCount": 212,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
"path": "shared/core/multi-agent-status/SKILL.md",
|
|
192
|
-
"sha256": "
|
|
192
|
+
"sha256": "3305fcce80bdad9ea0032e382276c5692a197749d9c012910daaea7a332bce0e"
|
|
193
193
|
},
|
|
194
194
|
{
|
|
195
195
|
"path": "shared/core/multi-agent-steer/SKILL.md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"skillCount":
|
|
3
|
+
"skillCount": 212,
|
|
4
4
|
"entries": [
|
|
5
5
|
{
|
|
6
6
|
"name": "accessibility-compliance-accessibility-audit",
|
|
@@ -959,6 +959,17 @@
|
|
|
959
959
|
"triggerPaths": [],
|
|
960
960
|
"relativePath": "shared/core/multi-agent-analysis/SKILL.md"
|
|
961
961
|
},
|
|
962
|
+
{
|
|
963
|
+
"name": "multi-agent-analysis-jira",
|
|
964
|
+
"description": "Turn a rendered analysis document into a Jira story tree: derive stories from the document's own rule ids, check coverage both ways, preview every byte, then create only what does not already exist. Use when an analysis is final and the work needs tickets.",
|
|
965
|
+
"platform": null,
|
|
966
|
+
"group": "core",
|
|
967
|
+
"plugin": null,
|
|
968
|
+
"invokeAs": "multi-agent-analysis-jira",
|
|
969
|
+
"triggerKeywords": [],
|
|
970
|
+
"triggerPaths": [],
|
|
971
|
+
"relativePath": "shared/core/multi-agent-analysis-jira/SKILL.md"
|
|
972
|
+
},
|
|
962
973
|
{
|
|
963
974
|
"name": "multi-agent-analysis-resolve",
|
|
964
975
|
"description": "Resolve the Section 20 Risks and Open Questions of an analysis v3 document one row at a time: up to 3 source-labeled answer candidates per row (evidence / repo / AI reasoned), chosen answer merged into the target section, doc updated in place with a changelog bump. Same locked decisions as /multi-ag",
|
|
@@ -981,6 +992,39 @@
|
|
|
981
992
|
"triggerPaths": [],
|
|
982
993
|
"relativePath": "shared/core/multi-agent-autopilot/SKILL.md"
|
|
983
994
|
},
|
|
995
|
+
{
|
|
996
|
+
"name": "multi-agent-autopilot-off",
|
|
997
|
+
"description": "Turn off continuous mode on this machine: the schedule is removed, work already running finishes, the repo selection is kept. Use when the machine should stop picking work up.",
|
|
998
|
+
"platform": null,
|
|
999
|
+
"group": "core",
|
|
1000
|
+
"plugin": null,
|
|
1001
|
+
"invokeAs": "multi-agent-autopilot-off",
|
|
1002
|
+
"triggerKeywords": [],
|
|
1003
|
+
"triggerPaths": [],
|
|
1004
|
+
"relativePath": "shared/core/multi-agent-autopilot-off/SKILL.md"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
"name": "multi-agent-autopilot-on",
|
|
1008
|
+
"description": "Turn on continuous mode on THIS machine: pick the repos it watches; labelled GitHub issues and assigned+labelled Jira items then run in a worktree and stop at an open PR. Use when the machine should pick work up unattended.",
|
|
1009
|
+
"platform": null,
|
|
1010
|
+
"group": "core",
|
|
1011
|
+
"plugin": null,
|
|
1012
|
+
"invokeAs": "multi-agent-autopilot-on",
|
|
1013
|
+
"triggerKeywords": [],
|
|
1014
|
+
"triggerPaths": [],
|
|
1015
|
+
"relativePath": "shared/core/multi-agent-autopilot-on/SKILL.md"
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
"name": "multi-agent-autopilot-status",
|
|
1019
|
+
"description": "Show what continuous mode is doing here: what runs at which phase, what is queued, what waits for an answer, what PRs it opened today. Use when asked whether autopilot is on.",
|
|
1020
|
+
"platform": null,
|
|
1021
|
+
"group": "core",
|
|
1022
|
+
"plugin": null,
|
|
1023
|
+
"invokeAs": "multi-agent-autopilot-status",
|
|
1024
|
+
"triggerKeywords": [],
|
|
1025
|
+
"triggerPaths": [],
|
|
1026
|
+
"relativePath": "shared/core/multi-agent-autopilot-status/SKILL.md"
|
|
1027
|
+
},
|
|
984
1028
|
{
|
|
985
1029
|
"name": "multi-agent-build-optimize",
|
|
986
1030
|
"description": "Wrapper that dispatches to xcode-build-orchestrator on iOS repos. Benchmarks the current Xcode build, runs compilation / project / SPM analyzers, produces a recommend-first optimization plan in .build-benchmark/, and re-benchmarks after the developer approves changes. Fails fast on non-iOS stacks. U",
|
|
@@ -1047,6 +1091,17 @@
|
|
|
1047
1091
|
"triggerPaths": [],
|
|
1048
1092
|
"relativePath": "shared/core/multi-agent-diff-explain/SKILL.md"
|
|
1049
1093
|
},
|
|
1094
|
+
{
|
|
1095
|
+
"name": "multi-agent-doctor",
|
|
1096
|
+
"description": "Health check for the installed pipeline: layout, preferences, credentials, hooks and host capabilities, each with one actionable step. Exit code is the verdict. Use when a run failed for an environmental reason, before a sync, or after an update.",
|
|
1097
|
+
"platform": null,
|
|
1098
|
+
"group": "core",
|
|
1099
|
+
"plugin": null,
|
|
1100
|
+
"invokeAs": "multi-agent-doctor",
|
|
1101
|
+
"triggerKeywords": [],
|
|
1102
|
+
"triggerPaths": [],
|
|
1103
|
+
"relativePath": "shared/core/multi-agent-doctor/SKILL.md"
|
|
1104
|
+
},
|
|
1050
1105
|
{
|
|
1051
1106
|
"name": "multi-agent-feedback",
|
|
1052
1107
|
"description": "Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows the payload and asks before sending. Use when a run went wrong and the maintainer should know.",
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Single source of truth for skills delivered to both Claude Code (`~/.claude/skills/`) and Copilot CLI (`~/.copilot/skills/`) by the installer.
|
|
4
4
|
|
|
5
|
-
**Total:**
|
|
5
|
+
**Total:** 212 skills (59 core + 153 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
|
|
6
6
|
|
|
7
7
|
## Directory layout
|
|
8
8
|
|
|
9
|
-
- **`core/`** -
|
|
9
|
+
- **`core/`** - 59 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
|
|
10
10
|
- **`external/`** - 153 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
|
|
11
11
|
- Install destinations (ADR-0009): Claude Code gets NO local copy of `external/` - it loads those skills from the `multi-agent-plugins` marketplace, namespaced (`ai-<stack>-toolkit:<name>`); only the two compliance catalogs from `core/` land in `~/.claude/skills/`. Copilot CLI and Codex CLI receive a flat copy filtered to the enabled stacks. `external/` remains the single authoring source that `build-stack-plugins.mjs` publishes from.
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
14
14
|
|
|
15
15
|
## Categories
|
|
16
16
|
|
|
17
|
-
- [Pipeline Orchestration](#pipeline-orchestration) -
|
|
17
|
+
- [Pipeline Orchestration](#pipeline-orchestration) - 59
|
|
18
18
|
- [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 90
|
|
19
19
|
- [Android / Kotlin](#android-kotlin) - 13
|
|
20
20
|
- [Web](#web) - 10
|
|
@@ -29,14 +29,19 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
29
29
|
| [`google-play-compliance`](./core/google-play-compliance/) | `core` | Google Play Store publication compliance - bundletool + aapt2 + apksigner orchestration + 21-rule policy catalog with Play Console error c |
|
|
30
30
|
| [`multi-agent`](./core/multi-agent/) | `core` | Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable |
|
|
31
31
|
| [`multi-agent-analysis`](./core/multi-agent-analysis/) | `core` | Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporat |
|
|
32
|
+
| [`multi-agent-analysis-jira`](./core/multi-agent-analysis-jira/) | `core` | Turn a rendered analysis document into a Jira story tree: derive stories from the document's own rule ids, check coverage both ways, preview |
|
|
32
33
|
| [`multi-agent-analysis-resolve`](./core/multi-agent-analysis-resolve/) | `core` | Resolve the Section 20 Risks and Open Questions of an analysis v3 document one row at a time: up to 3 source-labeled answer candidates per r |
|
|
33
34
|
| [`multi-agent-autopilot`](./core/multi-agent-autopilot/) | `core` | Launch any task in autopilot mode: skips every confirmation, runs end-to-end autonomously. Use when a task should run end to end with no con |
|
|
35
|
+
| [`multi-agent-autopilot-off`](./core/multi-agent-autopilot-off/) | `core` | Turn off continuous mode on this machine: the schedule is removed, work already running finishes, the repo selection is kept. Use when the m |
|
|
36
|
+
| [`multi-agent-autopilot-on`](./core/multi-agent-autopilot-on/) | `core` | Turn on continuous mode on THIS machine: pick the repos it watches; labelled GitHub issues and assigned+labelled Jira items then run in a wo |
|
|
37
|
+
| [`multi-agent-autopilot-status`](./core/multi-agent-autopilot-status/) | `core` | Show what continuous mode is doing here: what runs at which phase, what is queued, what waits for an answer, what PRs it opened today. Use w |
|
|
34
38
|
| [`multi-agent-build-optimize`](./core/multi-agent-build-optimize/) | `core` | Wrapper that dispatches to xcode-build-orchestrator on iOS repos. Benchmarks the current Xcode build, runs compilation / project / SPM analy |
|
|
35
39
|
| [`multi-agent-channels`](./core/multi-agent-channels/) | `core` | Multi-channel reporter - Jira/Confluence/Wiki/PR description. Multi-select channels + content, humanizer pass, reviewer-preserving Bitbuck |
|
|
36
40
|
| [`multi-agent-complaint-analysis`](./core/multi-agent-complaint-analysis/) | `core` | Customer-complaint triage. Ingests complaints (paste, csv/xlsx/txt/json file, Jira issue, Confluence URL), fetches Graylog evidence per trx/ |
|
|
37
41
|
| [`multi-agent-create-jira`](./core/multi-agent-create-jira/) | `core` | Create a standards-compliant Jira issue (Task / Bug / Story): asks the type, mines project conventions, drafts from a standard template with |
|
|
38
42
|
| [`multi-agent-design-check`](./core/multi-agent-design-check/) | `core` | Mock-mode vs Figma design audit (iOS / Android, local-only). Pick repo + module, gate on mock support, enumerate every state driver into a c |
|
|
39
43
|
| [`multi-agent-diff-explain`](./core/multi-agent-diff-explain/) | `core` | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
|
|
44
|
+
| [`multi-agent-doctor`](./core/multi-agent-doctor/) | `core` | Health check for the installed pipeline: layout, preferences, credentials, hooks and host capabilities, each with one actionable step. Exit |
|
|
40
45
|
| [`multi-agent-feedback`](./core/multi-agent-feedback/) | `core` | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
|
|
41
46
|
| [`multi-agent-forget`](./core/multi-agent-forget/) | `core` | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
|
|
42
47
|
| [`multi-agent-garbage-collect`](./core/multi-agent-garbage-collect/) | `core` | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
|
|
@@ -11,20 +11,44 @@ Show every active and completed task as a table.
|
|
|
11
11
|
|
|
12
12
|
## Steps
|
|
13
13
|
|
|
14
|
-
1. **
|
|
15
|
-
|
|
16
|
-
- `~/my-figma-app/.worktrees/`
|
|
17
|
-
- `~/my-ios-app/.worktrees/`
|
|
14
|
+
1. **Ask the producer, do not go looking.** One command answers the whole
|
|
15
|
+
question:
|
|
18
16
|
|
|
19
|
-
2. **Scan worktrees** - Read the `agent-state.json` file in each worktree directory:
|
|
20
17
|
```bash
|
|
21
|
-
|
|
18
|
+
node "$HOME/.claude/scripts/runs-index.mjs" # grouped table
|
|
19
|
+
node "$HOME/.claude/scripts/runs-index.mjs" --json # the same records
|
|
22
20
|
```
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
`runs-index.mjs` resolves through `lib/run-paths.sh` / `scripts/_run-paths.mjs`,
|
|
23
|
+
so it sees both directory layouts (`<project>/<id>/` and the flat `<id>/`),
|
|
24
|
+
the salvaged `artifacts/` copy Phase 6 leaves behind, and every spelling of a
|
|
25
|
+
task id - and it counts a run that exists in both layouts once. Do NOT
|
|
26
|
+
re-scan the tree by hand: the earlier instruction here listed three
|
|
27
|
+
hard-coded `.worktrees/` paths and a single `find` depth, and on a real
|
|
28
|
+
install that combination missed a quarter of the runs and double-counted
|
|
29
|
+
others. It also scanned worktrees for `agent-state.json`, which Phase 0 has
|
|
30
|
+
never written there ("never inside the worktree", `phases/phase-0-init.md`).
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
2. **Fields per run** (already in the output): `taskId`, `project`, `branch`,
|
|
33
|
+
`currentPhase`, `status`, `startedAt`, `worktreePath`, `prUrl`, `autopilot`,
|
|
34
|
+
`phases[]`, `tokens`, `estUsd`, `group`, plus `duplicateOf` when the run also
|
|
35
|
+
exists in the other layout and `salvaged` when its state is the Phase 6 copy.
|
|
36
|
+
|
|
37
|
+
3. **Groups are computed, not judged.** Report the `group` the producer
|
|
38
|
+
returns rather than re-deriving it.
|
|
39
|
+
|
|
40
|
+
| Group | Test | Action offered |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `waiting` - Waiting on you | `status == "awaiting_input"`, or a `pr.url`, or phase >= 6 | `resume #N` - the work landed, it needs your answer |
|
|
43
|
+
| `stopped` - Stopped mid-development | anything else past phase 0 | `resume #N` or `kill #N` |
|
|
44
|
+
| `question` - Left at a question | phase 0 | `garbage-collect --abandoned` - nothing was built |
|
|
45
|
+
| `unknown` - Status not recorded | no `status` field | say so; offer nothing |
|
|
46
|
+
|
|
47
|
+
A run with no `status` is **not** placed in an actionable group. Unknown is
|
|
48
|
+
not a finding, and calling it dead is the same false claim in the other
|
|
49
|
+
direction.
|
|
50
|
+
|
|
51
|
+
4. **Show as a table**, grouped per step 3:
|
|
28
52
|
```
|
|
29
53
|
🤖 Multi-Agent Tasks
|
|
30
54
|
|
package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/package_app.sh
CHANGED
|
@@ -19,7 +19,10 @@ else
|
|
|
19
19
|
BUILD_NUMBER=${BUILD_NUMBER:-1}
|
|
20
20
|
fi
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# read -ra, not ( $ARCHES ): a bare expansion into an array splits on IFS and
|
|
23
|
+
# then GLOBS each word, so an ARCHES carrying a * builds for whatever files are
|
|
24
|
+
# in the working directory.
|
|
25
|
+
read -ra ARCH_LIST <<< "${ARCHES:-}"
|
|
23
26
|
if [[ ${#ARCH_LIST[@]} -eq 0 ]]; then
|
|
24
27
|
HOST_ARCH=$(uname -m)
|
|
25
28
|
ARCH_LIST=("$HOST_ARCH")
|
|
@@ -15,7 +15,10 @@ fi
|
|
|
15
15
|
echo "Creating self-signed certificate '$CERT_NAME'..."
|
|
16
16
|
|
|
17
17
|
TEMP_CONFIG=$(mktemp)
|
|
18
|
-
trap
|
|
18
|
+
# Single-quoted on purpose: the path is read when the trap FIRES, not when it is
|
|
19
|
+
# installed. Double quotes bake in whatever the variable held at install time,
|
|
20
|
+
# which is a template users copy into scripts where the order differs.
|
|
21
|
+
trap 'rm -f "$TEMP_CONFIG"' EXIT
|
|
19
22
|
|
|
20
23
|
cat > "$TEMP_CONFIG" <<EOFCONF
|
|
21
24
|
[ req ]
|
|
@@ -17,7 +17,8 @@ echo "$APP_STORE_CONNECT_API_KEY_P8" | sed 's/\\n/\n/g' > /tmp/app-store-connect
|
|
|
17
17
|
trap 'rm -f /tmp/app-store-connect-key.p8 /tmp/${APP_NAME}Notarize.zip' EXIT
|
|
18
18
|
|
|
19
19
|
ARCHES_VALUE=${ARCHES:-"arm64 x86_64"}
|
|
20
|
-
|
|
20
|
+
# See package_app.sh: a bare expansion into an array globs as well as splits.
|
|
21
|
+
read -ra ARCH_LIST <<< "${ARCHES_VALUE}"
|
|
21
22
|
for ARCH in "${ARCH_LIST[@]}"; do
|
|
22
23
|
swift build -c release --arch "$ARCH"
|
|
23
24
|
done
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Auto-generated by `pipeline/scripts/build-skills-index.mjs` - do not hand-edit.
|
|
4
4
|
> Regenerate with `node pipeline/scripts/build-skills-index.mjs`.
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**212 skills** across 2 groups.
|
|
7
7
|
|
|
8
8
|
| Group | Name | Platform | Description |
|
|
9
9
|
|-------|------|----------|-------------|
|
|
@@ -94,14 +94,19 @@
|
|
|
94
94
|
| external | `monorepo-architect` | - | Expert in monorepo architecture, build systems, and dependency management at scale. Masters Nx, Turborepo, Bazel, and Lerna for efficient mu |
|
|
95
95
|
| core | `multi-agent` | - | Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable |
|
|
96
96
|
| core | `multi-agent-analysis` | - | Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporat |
|
|
97
|
+
| core | `multi-agent-analysis-jira` | - | Turn a rendered analysis document into a Jira story tree: derive stories from the document's own rule ids, check coverage both ways, preview |
|
|
97
98
|
| core | `multi-agent-analysis-resolve` | - | Resolve the Section 20 Risks and Open Questions of an analysis v3 document one row at a time: up to 3 source-labeled answer candidates per r |
|
|
98
99
|
| core | `multi-agent-autopilot` | - | Launch any task in autopilot mode: skips every confirmation, runs end-to-end autonomously. Use when a task should run end to end with no con |
|
|
100
|
+
| core | `multi-agent-autopilot-off` | - | Turn off continuous mode on this machine: the schedule is removed, work already running finishes, the repo selection is kept. Use when the m |
|
|
101
|
+
| core | `multi-agent-autopilot-on` | - | Turn on continuous mode on THIS machine: pick the repos it watches; labelled GitHub issues and assigned+labelled Jira items then run in a wo |
|
|
102
|
+
| core | `multi-agent-autopilot-status` | - | Show what continuous mode is doing here: what runs at which phase, what is queued, what waits for an answer, what PRs it opened today. Use w |
|
|
99
103
|
| core | `multi-agent-build-optimize` | - | Wrapper that dispatches to xcode-build-orchestrator on iOS repos. Benchmarks the current Xcode build, runs compilation / project / SPM analy |
|
|
100
104
|
| core | `multi-agent-channels` | - | Multi-channel reporter - Jira/Confluence/Wiki/PR description. Multi-select channels + content, humanizer pass, reviewer-preserving Bitbuck |
|
|
101
105
|
| core | `multi-agent-complaint-analysis` | - | Customer-complaint triage. Ingests complaints (paste, csv/xlsx/txt/json file, Jira issue, Confluence URL), fetches Graylog evidence per trx/ |
|
|
102
106
|
| core | `multi-agent-create-jira` | - | Create a standards-compliant Jira issue (Task / Bug / Story): asks the type, mines project conventions, drafts from a standard template with |
|
|
103
107
|
| core | `multi-agent-design-check` | - | Mock-mode vs Figma design audit (iOS / Android, local-only). Pick repo + module, gate on mock support, enumerate every state driver into a c |
|
|
104
108
|
| core | `multi-agent-diff-explain` | - | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
|
|
109
|
+
| core | `multi-agent-doctor` | - | Health check for the installed pipeline: layout, preferences, credentials, hooks and host capabilities, each with one actionable step. Exit |
|
|
105
110
|
| core | `multi-agent-feedback` | - | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
|
|
106
111
|
| core | `multi-agent-forget` | - | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
|
|
107
112
|
| core | `multi-agent-garbage-collect` | - | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
|