@mmerterden/multi-agent-pipeline 20.29.0 → 20.33.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 +86 -0
- package/README.md +50 -5
- package/README.tr.md +64 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +9 -9
- package/install/_unattended-profile.mjs +61 -0
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +19 -3
- package/install/codex.mjs +2 -0
- package/install/copilot.mjs +13 -0
- package/install/index.mjs +10 -1
- package/install/templates/claude-hooks.json +12 -1
- package/manifest.json +230 -88
- package/package.json +3 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +13 -15
- package/pipeline/commands/multi-agent/office-off/SKILL.md +27 -0
- package/pipeline/commands/multi-agent/office-on/SKILL.md +51 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +29 -0
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -1
- package/pipeline/commands/multi-agent/review/SKILL.md +4 -3
- package/pipeline/commands/multi-agent/scenario-audit/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/serve/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +15 -8
- package/pipeline/commands/multi-agent/steer/SKILL.md +10 -9
- package/pipeline/commands/multi-agent/sync/SKILL.md +15 -3
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +29 -3
- package/pipeline/contract/CHANGELOG.md +108 -0
- package/pipeline/contract/README.md +72 -12
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/launch-plan.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +6 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +6 -1
- package/pipeline/contract/fixtures/runs-old-schema.json +6 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +5 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +6 -1
- package/pipeline/contract/fixtures/runs-running.json +6 -1
- package/pipeline/contract/manifest.json +506 -27
- package/pipeline/contract/types/index.d.ts +829 -4
- package/pipeline/lib/bg-launch.mjs +106 -0
- package/pipeline/lib/code-digest.mjs +49 -0
- package/pipeline/lib/credential-resolve.mjs +42 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +5 -4
- package/pipeline/multi-agent-refs/features/office-threat-model.md +109 -0
- package/pipeline/multi-agent-refs/features/unattended-security.md +1 -1
- package/pipeline/multi-agent-refs/help/en.md +3 -0
- package/pipeline/multi-agent-refs/help/tr.md +3 -0
- package/pipeline/multi-agent-refs/phases/modes.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +16 -10
- package/pipeline/multi-agent-refs/review-standalone-tracker.md +37 -0
- package/pipeline/multi-agent-refs/tracker-contract.md +8 -3
- package/pipeline/office/NOTICE.md +45 -0
- package/pipeline/office/assets/ARCHIVE_BOXES.png +0 -0
- package/pipeline/office/assets/BIN.png +0 -0
- package/pipeline/office/assets/BOOKSHELF.png +0 -0
- package/pipeline/office/assets/CACTUS.png +0 -0
- package/pipeline/office/assets/CLOCK.png +0 -0
- package/pipeline/office/assets/COFFEE.png +0 -0
- package/pipeline/office/assets/COFFEE_MACHINE.png +0 -0
- package/pipeline/office/assets/COFFEE_TABLE.png +0 -0
- package/pipeline/office/assets/COUNTER_SINK.png +0 -0
- package/pipeline/office/assets/CUSHIONED_BENCH.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/DESK_FRONT.png +0 -0
- package/pipeline/office/assets/DESK_SIDE.png +0 -0
- package/pipeline/office/assets/DEVICE_STAND.png +0 -0
- package/pipeline/office/assets/DOUBLE_BOOKSHELF.png +0 -0
- package/pipeline/office/assets/FILING_CABINET.png +0 -0
- package/pipeline/office/assets/FRIDGE.png +0 -0
- package/pipeline/office/assets/HANGING_PLANT.png +0 -0
- package/pipeline/office/assets/LARGE_PAINTING.png +0 -0
- package/pipeline/office/assets/LARGE_PLANT.png +0 -0
- package/pipeline/office/assets/MAILBOX.png +0 -0
- package/pipeline/office/assets/MOP_BUCKET.png +0 -0
- package/pipeline/office/assets/PC_BACK.png +0 -0
- package/pipeline/office/assets/PC_FRONT_OFF.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_1.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_2.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_3.png +0 -0
- package/pipeline/office/assets/PC_SIDE.png +0 -0
- package/pipeline/office/assets/PLANT.png +0 -0
- package/pipeline/office/assets/PLANT_2.png +0 -0
- package/pipeline/office/assets/POT.png +0 -0
- package/pipeline/office/assets/PRINTER.png +0 -0
- package/pipeline/office/assets/RECEPTION_DESK.png +0 -0
- package/pipeline/office/assets/SERVER_RACK.png +0 -0
- package/pipeline/office/assets/SERVER_RACK_2.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING_2.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_SIDE.png +0 -0
- package/pipeline/office/assets/SOFA_BACK.png +0 -0
- package/pipeline/office/assets/SOFA_FRONT.png +0 -0
- package/pipeline/office/assets/SOFA_SIDE.png +0 -0
- package/pipeline/office/assets/TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/TOILET_STALL.png +0 -0
- package/pipeline/office/assets/VENDING.png +0 -0
- package/pipeline/office/assets/WASHBASIN.png +0 -0
- package/pipeline/office/assets/WATER_COOLER.png +0 -0
- package/pipeline/office/assets/WHITEBOARD.png +0 -0
- package/pipeline/office/assets/WOODEN_BENCH.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/char_0.png +0 -0
- package/pipeline/office/assets/char_1.png +0 -0
- package/pipeline/office/assets/char_2.png +0 -0
- package/pipeline/office/assets/char_3.png +0 -0
- package/pipeline/office/assets/char_4.png +0 -0
- package/pipeline/office/assets/char_5.png +0 -0
- package/pipeline/office/assets/floor_0.png +0 -0
- package/pipeline/office/assets/floor_1.png +0 -0
- package/pipeline/office/assets/floor_2.png +0 -0
- package/pipeline/office/assets/floor_3.png +0 -0
- package/pipeline/office/assets/floor_4.png +0 -0
- package/pipeline/office/assets/floor_5.png +0 -0
- package/pipeline/office/assets/floor_6.png +0 -0
- package/pipeline/office/assets/floor_7.png +0 -0
- package/pipeline/office/assets/floor_8.png +0 -0
- package/pipeline/office/assets/wall_0.png +0 -0
- package/pipeline/office/index.html +8431 -0
- package/pipeline/office/toolkit.json +3 -0
- package/pipeline/schemas/activity-chat.schema.json +78 -0
- package/pipeline/schemas/activity-roles.json +48 -0
- package/pipeline/schemas/activity-roles.schema.json +49 -0
- package/pipeline/schemas/activity.schema.json +613 -0
- package/pipeline/schemas/agent-state.schema.json +493 -115
- package/pipeline/schemas/autopilot-on-request.schema.json +33 -0
- package/pipeline/schemas/autopilot-on.schema.json +111 -0
- package/pipeline/schemas/contract-error.schema.json +45 -4
- package/pipeline/schemas/issue-maturity.schema.json +68 -0
- package/pipeline/schemas/job-change.schema.json +72 -0
- package/pipeline/schemas/job-request.schema.json +38 -0
- package/pipeline/schemas/launch.json +4 -4
- package/pipeline/schemas/ledger.schema.json +202 -0
- package/pipeline/schemas/office-credentials.schema.json +71 -0
- package/pipeline/schemas/office-jobs.json +253 -0
- package/pipeline/schemas/office-jobs.schema.json +62 -0
- package/pipeline/schemas/office-memory.schema.json +105 -0
- package/pipeline/schemas/office-settings-request.schema.json +30 -0
- package/pipeline/schemas/office-settings.schema.json +67 -0
- package/pipeline/schemas/permissions.schema.json +47 -0
- package/pipeline/schemas/phases.json +10 -2
- package/pipeline/schemas/phases.schema.json +86 -19
- package/pipeline/schemas/prefs.schema.json +19 -0
- package/pipeline/schemas/prs.schema.json +78 -0
- package/pipeline/schemas/prune-logs-request.schema.json +26 -0
- package/pipeline/schemas/prune-logs.schema.json +52 -0
- package/pipeline/schemas/repo-change.schema.json +54 -0
- package/pipeline/schemas/repo-request.schema.json +29 -0
- package/pipeline/schemas/repos.schema.json +49 -1
- package/pipeline/schemas/review-verdict-request.schema.json +20 -0
- package/pipeline/schemas/review-verdict.schema.json +62 -0
- package/pipeline/schemas/routine-delete.schema.json +59 -0
- package/pipeline/schemas/routines.schema.json +38 -0
- package/pipeline/schemas/run-evidence.schema.json +29 -0
- package/pipeline/schemas/run-results.schema.json +218 -0
- package/pipeline/schemas/runs-index.schema.json +257 -46
- package/pipeline/schemas/service-read.schema.json +58 -0
- package/pipeline/schemas/stack-change.schema.json +82 -0
- package/pipeline/schemas/stack-request.schema.json +33 -0
- package/pipeline/schemas/stack.schema.json +59 -0
- package/pipeline/schemas/steer-request.schema.json +25 -0
- package/pipeline/schemas/steer.schema.json +59 -0
- package/pipeline/schemas/system-status.schema.json +111 -0
- package/pipeline/schemas/tool-call-request.schema.json +19 -0
- package/pipeline/schemas/tool-call.schema.json +47 -0
- package/pipeline/schemas/tools.schema.json +65 -0
- package/pipeline/schemas/unattended-profile.json +7 -2
- package/pipeline/scripts/_active-run.mjs +2 -1
- package/pipeline/scripts/_cost.mjs +66 -1
- package/pipeline/scripts/_office-smoke-lib.sh +115 -0
- package/pipeline/scripts/_spawn-async.mjs +77 -0
- package/pipeline/scripts/_transcript-tail.mjs +274 -0
- package/pipeline/scripts/activity-snapshot.mjs +1223 -0
- package/pipeline/scripts/autopilot-control.mjs +214 -9
- package/pipeline/scripts/autopilot-runner.mjs +7 -5
- package/pipeline/scripts/contract-server.mjs +1853 -197
- package/pipeline/scripts/cost-analyze.mjs +2 -53
- package/pipeline/scripts/cost-table.json +1 -0
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -1
- package/pipeline/scripts/job-launch.mjs +477 -0
- package/pipeline/scripts/launch-request.mjs +94 -9
- package/pipeline/scripts/office-build.mjs +226 -0
- package/pipeline/scripts/office-control.mjs +410 -0
- package/pipeline/scripts/office-credentials.mjs +106 -0
- package/pipeline/scripts/office-ledger.mjs +409 -0
- package/pipeline/scripts/office-memory.mjs +200 -0
- package/pipeline/scripts/office-prune.mjs +125 -0
- package/pipeline/scripts/office-settings.mjs +145 -0
- package/pipeline/scripts/phase-tracker.sh +12 -5
- package/pipeline/scripts/prune-logs.sh +53 -2
- package/pipeline/scripts/routine-registry.mjs +42 -6
- package/pipeline/scripts/run-results.mjs +373 -0
- package/pipeline/scripts/runs-index.mjs +62 -2
- package/pipeline/scripts/service-read.mjs +244 -0
- package/pipeline/scripts/stack-toggle.mjs +285 -0
- package/pipeline/scripts/steer-run.mjs +97 -0
- package/pipeline/scripts/system-status.mjs +196 -0
- package/pipeline/scripts/task-mirror.mjs +150 -0
- package/pipeline/scripts/task-mirror.sh +20 -0
- package/pipeline/scripts/toolkit-tools.mjs +625 -0
- package/pipeline/scripts/transcript-index.mjs +4 -1
- package/pipeline/scripts/uninstall.mjs +3 -0
- package/pipeline/scripts/work-items.mjs +473 -0
- package/pipeline/skills/.skill-manifest.json +23 -11
- package/pipeline/skills/.skills-index.json +34 -1
- package/pipeline/skills/shared/README.md +6 -3
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +13 -15
- package/pipeline/skills/shared/core/multi-agent-office-off/SKILL.md +16 -0
- package/pipeline/skills/shared/core/multi-agent-office-on/SKILL.md +21 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +17 -0
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +4 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +19 -0
- package/pipeline/skills/shared/core/multi-agent-scenario-audit/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +14 -6
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +10 -9
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-test-gen/SKILL.md +26 -3
- package/pipeline/skills/skills-index.md +4 -1
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file work-items.mjs - the work list and the PR list a client shows next
|
|
4
|
+
* to the run list, built on what the pipeline already uses:
|
|
5
|
+
*
|
|
6
|
+
* maturity <key> lib/issue-fetcher.sh <key>, the fetch and maturity score
|
|
7
|
+
* /multi-agent:jira, /multi-agent:issue and Phase 0 run;
|
|
8
|
+
* title, type, status, score, blockers, warnings, summary
|
|
9
|
+
* and a description excerpt, scrubbed
|
|
10
|
+
* prs open pull requests on GitHub where you are asked to
|
|
11
|
+
* review or are the author (`gh search prs`, the queries
|
|
12
|
+
* /multi-agent:review lists). Bitbucket is reported as a
|
|
13
|
+
* source that is not implemented, never as an empty list
|
|
14
|
+
* verdict <task> the review verdict of a finished /multi-agent:review run
|
|
15
|
+
* whose every finding was triaged (accepted, rejected or
|
|
16
|
+
* deferred); a running review or one with a pending
|
|
17
|
+
* finding is refused. The preview carries findingsDigest,
|
|
18
|
+
* a hash of the findings the decision was made from, so a
|
|
19
|
+
* confirmation can be refused when they changed since:
|
|
20
|
+
* `preview` computes it the way lib/post-pr-review.sh does
|
|
21
|
+
* (approve when no accepted blocking or important finding,
|
|
22
|
+
* else needs-work) without posting; `post` runs
|
|
23
|
+
* post-pr-review.sh itself, which posts the inline comments
|
|
24
|
+
* and the verdict with the user's own gh or Bitbucket
|
|
25
|
+
* credential
|
|
26
|
+
*
|
|
27
|
+
* The scripts run as child processes without blocking the caller's event
|
|
28
|
+
* loop.
|
|
29
|
+
*
|
|
30
|
+
* The list of assigned issues is issues.mjs (`GET /v1/issues`); nothing here
|
|
31
|
+
* fetches it a second time. Credentials are only ever read by the scripts
|
|
32
|
+
* above through credential-store.sh; no value reaches this process's output.
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* work-items.mjs maturity <key>
|
|
36
|
+
* work-items.mjs prs
|
|
37
|
+
* work-items.mjs verdict <task-id> [--post]
|
|
38
|
+
*
|
|
39
|
+
* MA_GH_BIN stands in for gh, MA_ISSUE_FETCHER and MA_POST_PR_REVIEW for the
|
|
40
|
+
* two scripts (tests).
|
|
41
|
+
*
|
|
42
|
+
* Exit: 0 printed, 1 refused, 2 usage, 3 no such run or issue.
|
|
43
|
+
*
|
|
44
|
+
* @module pipeline/scripts/work-items
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import { createHash } from "node:crypto";
|
|
48
|
+
import { existsSync } from "node:fs";
|
|
49
|
+
import { dirname, join } from "node:path";
|
|
50
|
+
import { fileURLToPath } from "node:url";
|
|
51
|
+
import { scrub } from "./_redact.mjs";
|
|
52
|
+
import { selectRuns } from "./runs-index.mjs";
|
|
53
|
+
import { createRedactor } from "./transcript-index.mjs";
|
|
54
|
+
import { spawnAsync } from "./_spawn-async.mjs";
|
|
55
|
+
import { readRegularJson } from "../lib/regular-file.mjs";
|
|
56
|
+
import { runMain } from "../lib/fatal.mjs";
|
|
57
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
58
|
+
import { resolveTokenAsync } from "../lib/credential-resolve.mjs";
|
|
59
|
+
import { homedir } from "node:os";
|
|
60
|
+
|
|
61
|
+
export const CONTRACT_VERSION = "1.1.0";
|
|
62
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
63
|
+
/** A Jira key, `owner/repo#N` or `#N`: the structured inputs launch accepts too. */
|
|
64
|
+
export const ISSUE_KEY =
|
|
65
|
+
/^(?:[A-Z][A-Z0-9_]{0,15}-[0-9]{1,9}|[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}#[0-9]{1,9}|#[0-9]{1,9})$/;
|
|
66
|
+
const TIMEOUT_MS = 60_000;
|
|
67
|
+
|
|
68
|
+
export class WorkError extends Error {
|
|
69
|
+
constructor(code, message) {
|
|
70
|
+
super(message);
|
|
71
|
+
this.code = code;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const libScript = (env, name, override) =>
|
|
76
|
+
env[override] ||
|
|
77
|
+
[join(HERE, "..", "lib", name), join(HERE, "..", "..", "lib", name)].find((p) => existsSync(p));
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The maturity check for one issue, from issue-fetcher.sh.
|
|
81
|
+
*
|
|
82
|
+
* @returns {{contractVersion, key, title, type, status, kind, maturity, description}}
|
|
83
|
+
*/
|
|
84
|
+
export async function issueMaturity(key, env = process.env) {
|
|
85
|
+
if (typeof key !== "string" || !ISSUE_KEY.test(key)) {
|
|
86
|
+
throw new WorkError("bad-key", "the key is not a Jira key, owner/repo#N or #N");
|
|
87
|
+
}
|
|
88
|
+
const script = libScript(env, "issue-fetcher.sh", "MA_ISSUE_FETCHER");
|
|
89
|
+
if (!script) throw new WorkError("not-installed", "issue-fetcher.sh is not installed");
|
|
90
|
+
const r = await spawnAsync("bash", [script, key], { env, timeoutMs: TIMEOUT_MS });
|
|
91
|
+
let d;
|
|
92
|
+
try {
|
|
93
|
+
d = JSON.parse(r.stdout);
|
|
94
|
+
} catch {
|
|
95
|
+
throw new WorkError("fetch-failed", "the issue could not be fetched");
|
|
96
|
+
}
|
|
97
|
+
if (!d || typeof d !== "object")
|
|
98
|
+
throw new WorkError("fetch-failed", "the issue could not be fetched");
|
|
99
|
+
const R = createRedactor();
|
|
100
|
+
const m = d.maturity && typeof d.maturity === "object" ? d.maturity : {};
|
|
101
|
+
const codes = (v) => (Array.isArray(v) ? v.map((x) => String(x?.code ?? x)).slice(0, 20) : []);
|
|
102
|
+
return {
|
|
103
|
+
contractVersion: CONTRACT_VERSION,
|
|
104
|
+
key,
|
|
105
|
+
kind: ["jira", "github", "freetext"].includes(d.kind) ? d.kind : "freetext",
|
|
106
|
+
title: R.excerpt(d.title ?? "", 200),
|
|
107
|
+
type: d.type ? String(d.type) : null,
|
|
108
|
+
status: d.status ? String(d.status) : null,
|
|
109
|
+
maturity: {
|
|
110
|
+
score: Number.isFinite(m.score) ? m.score : null,
|
|
111
|
+
blockers: codes(m.blockers),
|
|
112
|
+
warnings: codes(m.warnings),
|
|
113
|
+
summary: m.summary ? R.excerpt(m.summary, 400) : null,
|
|
114
|
+
},
|
|
115
|
+
description: d.description ? R.excerpt(d.description, 2000) : null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The GitHub accounts `gh` is logged in to, as host and login: a personal and
|
|
121
|
+
* a corporate account are asked one by one, each with its own token, so a
|
|
122
|
+
* review request on either shows. Empty when `gh auth status` says nothing
|
|
123
|
+
* readable; the caller then asks with gh's active account alone.
|
|
124
|
+
*/
|
|
125
|
+
async function ghAccounts(env) {
|
|
126
|
+
const gh = env.MA_GH_BIN || "gh";
|
|
127
|
+
const r = await spawnAsync(gh, ["auth", "status"], {
|
|
128
|
+
env,
|
|
129
|
+
timeoutMs: TIMEOUT_MS,
|
|
130
|
+
withStderr: true,
|
|
131
|
+
});
|
|
132
|
+
const text = r.stdout || "";
|
|
133
|
+
const out = [];
|
|
134
|
+
for (const m of text.matchAll(/Logged in to (\S+) account ([A-Za-z0-9_.-]+)/g))
|
|
135
|
+
if (!out.some((a) => a.host === m[1] && a.login === m[2]))
|
|
136
|
+
out.push({ host: m[1], login: m[2] });
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* gh's token for one account, carried in the environment only (GH_TOKEN, and
|
|
142
|
+
* GH_ENTERPRISE_TOKEN for an Enterprise Server host), never on a command line.
|
|
143
|
+
*/
|
|
144
|
+
async function accountEnv(env, account) {
|
|
145
|
+
const gh = env.MA_GH_BIN || "gh";
|
|
146
|
+
const r = await spawnAsync(
|
|
147
|
+
gh,
|
|
148
|
+
["auth", "token", "--hostname", account.host, "--user", account.login],
|
|
149
|
+
{ env, timeoutMs: TIMEOUT_MS },
|
|
150
|
+
);
|
|
151
|
+
const token = (r.stdout || "").trim();
|
|
152
|
+
if (r.status !== 0 || !token) return null;
|
|
153
|
+
// gh reads GH_TOKEN for github.com and GH_ENTERPRISE_TOKEN for a GitHub
|
|
154
|
+
// Enterprise Server host, so an enterprise account gets both.
|
|
155
|
+
const enterprise = !/^(github\.com|[a-z0-9-]+\.ghe\.com)$/i.test(account.host);
|
|
156
|
+
return {
|
|
157
|
+
...env,
|
|
158
|
+
GH_TOKEN: token,
|
|
159
|
+
GH_HOST: account.host,
|
|
160
|
+
...(enterprise ? { GH_ENTERPRISE_TOKEN: token } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Open pull requests on the configured Bitbucket Server where this user is a
|
|
166
|
+
* reviewer or the author, from its dashboard API. The token comes from the
|
|
167
|
+
* credential store and travels in the request header only.
|
|
168
|
+
*/
|
|
169
|
+
async function bitbucketPrs(env) {
|
|
170
|
+
let host = env.MA_BITBUCKET_HOST || null;
|
|
171
|
+
if (!host) {
|
|
172
|
+
try {
|
|
173
|
+
const prefs = readRegularJson(
|
|
174
|
+
env.MULTI_AGENT_PREFS ||
|
|
175
|
+
join(env.HOME || homedir(), ".claude", "multi-agent-preferences.json"),
|
|
176
|
+
null,
|
|
177
|
+
);
|
|
178
|
+
host = prefs?.global?.hosts?.bitbucket ?? null;
|
|
179
|
+
} catch {
|
|
180
|
+
host = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (typeof host !== "string" || !host) return { ok: false, items: [], reason: "not configured" };
|
|
184
|
+
// The token travels in a header, so only over TLS: a host written with
|
|
185
|
+
// http:// is refused unless it is this machine's own loopback.
|
|
186
|
+
const loopback = /^http:\/\/(127\.0\.0\.1|localhost)(:\d+)?(\/|$)/i.test(host);
|
|
187
|
+
if (/^http:\/\//i.test(host) && !loopback)
|
|
188
|
+
return { ok: false, items: [], reason: "https required" };
|
|
189
|
+
const base = /^https?:\/\//i.test(host) ? host.replace(/\/+$/, "") : `https://${host}`;
|
|
190
|
+
const token =
|
|
191
|
+
env.MA_BITBUCKET_TOKEN ||
|
|
192
|
+
(await resolveTokenAsync("bitbucket_token", "BITBUCKET_TOKEN", { env }));
|
|
193
|
+
if (!token) return { ok: false, items: [], reason: "no token in the credential store" };
|
|
194
|
+
// The same rule as credential-inventory.sh: Basic with the stored user (an
|
|
195
|
+
// app password), else the token as a bearer.
|
|
196
|
+
const user =
|
|
197
|
+
env.MA_BITBUCKET_USER || (await resolveTokenAsync("bitbucket_user", "BITBUCKET_USER", { env }));
|
|
198
|
+
const authorization = user
|
|
199
|
+
? "Basic " + Buffer.from(`${user}:${token}`).toString("base64")
|
|
200
|
+
: `Bearer ${token}`;
|
|
201
|
+
const items = [];
|
|
202
|
+
try {
|
|
203
|
+
for (const role of ["REVIEWER", "AUTHOR"]) {
|
|
204
|
+
const res = await fetch(
|
|
205
|
+
`${base}/rest/api/1.0/dashboard/pull-requests?state=OPEN&role=${role}&limit=50`,
|
|
206
|
+
{
|
|
207
|
+
headers: { Authorization: authorization, Accept: "application/json" },
|
|
208
|
+
redirect: "error",
|
|
209
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
210
|
+
},
|
|
211
|
+
);
|
|
212
|
+
if (res.status === 401 || res.status === 403)
|
|
213
|
+
return { ok: false, items: [], reason: "the token was refused" };
|
|
214
|
+
if (!res.ok) return { ok: false, items: [], reason: `Bitbucket answered ${res.status}` };
|
|
215
|
+
const doc = await res.json();
|
|
216
|
+
for (const p of Array.isArray(doc?.values) ? doc.values : []) {
|
|
217
|
+
const repo = p?.toRef?.repository;
|
|
218
|
+
items.push({
|
|
219
|
+
source: "bitbucket",
|
|
220
|
+
account: null,
|
|
221
|
+
repo: String(repo ? `${repo.project?.key ?? ""}/${repo.slug ?? ""}` : ""),
|
|
222
|
+
number: Number(p?.id) || 0,
|
|
223
|
+
title: scrub(String(p?.title ?? "")),
|
|
224
|
+
url: typeof p?.links?.self?.[0]?.href === "string" ? p.links.self[0].href : null,
|
|
225
|
+
author: p?.author?.user?.name ? String(p.author.user.name) : null,
|
|
226
|
+
updatedAt: Number.isFinite(p?.updatedDate) ? new Date(p.updatedDate).toISOString() : null,
|
|
227
|
+
role: role === "REVIEWER" ? "reviewer" : "author",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch (e) {
|
|
232
|
+
const why = String(e?.cause?.message ?? e?.message ?? "");
|
|
233
|
+
return {
|
|
234
|
+
ok: false,
|
|
235
|
+
items: [],
|
|
236
|
+
reason: /redirect/i.test(why) ? "redirect refused" : "unreachable (VPN?)",
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return { ok: true, items };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function ghSearch(env, role, account = null) {
|
|
243
|
+
const gh = env.MA_GH_BIN || "gh";
|
|
244
|
+
const flag = role === "reviewer" ? "--review-requested=@me" : "--author=@me";
|
|
245
|
+
const r = await spawnAsync(
|
|
246
|
+
gh,
|
|
247
|
+
[
|
|
248
|
+
"search",
|
|
249
|
+
"prs",
|
|
250
|
+
"--state",
|
|
251
|
+
"open",
|
|
252
|
+
flag,
|
|
253
|
+
"--json",
|
|
254
|
+
"number,title,url,repository,author,updatedAt",
|
|
255
|
+
"--limit",
|
|
256
|
+
"50",
|
|
257
|
+
],
|
|
258
|
+
{ env, timeoutMs: TIMEOUT_MS },
|
|
259
|
+
);
|
|
260
|
+
if (r.error || r.status !== 0) return { ok: false, items: [] };
|
|
261
|
+
try {
|
|
262
|
+
const rows = JSON.parse(r.stdout);
|
|
263
|
+
return {
|
|
264
|
+
ok: true,
|
|
265
|
+
items: (Array.isArray(rows) ? rows : []).map((p) => ({
|
|
266
|
+
source: "github",
|
|
267
|
+
account,
|
|
268
|
+
repo: String(p?.repository?.nameWithOwner ?? p?.repository?.name ?? ""),
|
|
269
|
+
number: Number(p?.number) || 0,
|
|
270
|
+
title: scrub(String(p?.title ?? "")),
|
|
271
|
+
url: typeof p?.url === "string" ? p.url : null,
|
|
272
|
+
author: p?.author?.login ? String(p.author.login) : null,
|
|
273
|
+
updatedAt: typeof p?.updatedAt === "string" ? p.updatedAt : null,
|
|
274
|
+
role,
|
|
275
|
+
})),
|
|
276
|
+
};
|
|
277
|
+
} catch {
|
|
278
|
+
return { ok: false, items: [] };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Open pull requests on every host the pipeline can list, one list: every
|
|
284
|
+
* GitHub account gh is logged in to (personal and corporate alike) and the
|
|
285
|
+
* configured Bitbucket Server.
|
|
286
|
+
*/
|
|
287
|
+
export async function pullRequests({ env = process.env, redact = false } = {}) {
|
|
288
|
+
const accounts = await ghAccounts(env);
|
|
289
|
+
// gh before `auth token --user` hands out no token per account; then the
|
|
290
|
+
// active account answers alone, as a plain `gh search` does.
|
|
291
|
+
const envs = await Promise.all(accounts.map((a) => accountEnv(env, a)));
|
|
292
|
+
const perAccount = envs.some(Boolean);
|
|
293
|
+
const github = [];
|
|
294
|
+
if (accounts.length && perAccount) {
|
|
295
|
+
for (const [i, a] of accounts.entries()) {
|
|
296
|
+
const aEnv = envs[i];
|
|
297
|
+
if (!aEnv) {
|
|
298
|
+
github.push({
|
|
299
|
+
source: `github:${a.login}`,
|
|
300
|
+
ok: false,
|
|
301
|
+
items: [],
|
|
302
|
+
reason: "gh has no token for it",
|
|
303
|
+
});
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const [rv, au] = await Promise.all([
|
|
307
|
+
ghSearch(aEnv, "reviewer", a.login),
|
|
308
|
+
ghSearch(aEnv, "author", a.login),
|
|
309
|
+
]);
|
|
310
|
+
github.push({
|
|
311
|
+
source: `github:${a.login}`,
|
|
312
|
+
ok: rv.ok || au.ok,
|
|
313
|
+
items: [...rv.items, ...au.items],
|
|
314
|
+
reason: rv.ok || au.ok ? undefined : "gh could not list pull requests",
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
const [rv, au] = await Promise.all([ghSearch(env, "reviewer"), ghSearch(env, "author")]);
|
|
319
|
+
github.push({
|
|
320
|
+
source: "github",
|
|
321
|
+
ok: rv.ok || au.ok,
|
|
322
|
+
items: [...rv.items, ...au.items],
|
|
323
|
+
reason: rv.ok || au.ok ? undefined : "gh could not list pull requests",
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
const bitbucket = { source: "bitbucket", ...(await bitbucketPrs(env)) };
|
|
327
|
+
const seen = new Set();
|
|
328
|
+
const items = [];
|
|
329
|
+
const sources = [];
|
|
330
|
+
// A redacted answer (the phone's) names no login: accounts become numbers.
|
|
331
|
+
let accountNo = 0;
|
|
332
|
+
for (const src of [...github, bitbucket]) {
|
|
333
|
+
const label = redact && src.source.startsWith("github:") ? `github:${++accountNo}` : src.source;
|
|
334
|
+
let count = 0;
|
|
335
|
+
for (const p of src.items) {
|
|
336
|
+
const k = `${p.source}:${p.repo}#${p.number}`;
|
|
337
|
+
if (seen.has(k)) continue;
|
|
338
|
+
seen.add(k);
|
|
339
|
+
count++;
|
|
340
|
+
items.push(redact ? { ...p, url: null, account: null, author: null } : p);
|
|
341
|
+
}
|
|
342
|
+
sources.push(
|
|
343
|
+
src.ok
|
|
344
|
+
? { source: label, ok: true, count }
|
|
345
|
+
: { source: label, ok: false, count: 0, reason: src.reason || "unreadable" },
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
items.sort((a, b) => ((a.updatedAt ?? "") < (b.updatedAt ?? "") ? 1 : -1));
|
|
349
|
+
return { contractVersion: CONTRACT_VERSION, items, sources };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function stateOf(taskId) {
|
|
353
|
+
const { found, records } = selectRuns({ taskId });
|
|
354
|
+
if (!found || !records.length) throw new WorkError("no-run", "no such run");
|
|
355
|
+
const dir = records[0].dir;
|
|
356
|
+
for (const p of [join(dir, "agent-state.json"), join(dir, "artifacts", "agent-state.json")]) {
|
|
357
|
+
if (existsSync(p)) return { taskId: records[0].taskId, state: readRegularJson(p, null) ?? {} };
|
|
358
|
+
}
|
|
359
|
+
throw new WorkError("no-review", "the run has no review to publish");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const FINISHED = new Set(["complete", "completed", "done"]);
|
|
363
|
+
const TRIAGED = new Set(["accepted", "rejected", "deferred"]);
|
|
364
|
+
|
|
365
|
+
/** A hash of the findings a decision is made from, in their recorded order. */
|
|
366
|
+
export function findingsDigest(findings) {
|
|
367
|
+
const rows = findings.map((f) => [
|
|
368
|
+
f?.status ?? null,
|
|
369
|
+
f?.severity ?? null,
|
|
370
|
+
f?.file ?? null,
|
|
371
|
+
Number.isInteger(f?.line) ? f.line : null,
|
|
372
|
+
f?.issue ?? f?.title ?? null,
|
|
373
|
+
]);
|
|
374
|
+
return createHash("sha256").update(JSON.stringify(rows)).digest("hex");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* The verdict post-pr-review.sh would post for a review run, without posting.
|
|
379
|
+
* Refused when the run has no review input, its findings were never written,
|
|
380
|
+
* the run has not finished, or any finding is still untriaged.
|
|
381
|
+
*/
|
|
382
|
+
export function reviewVerdictPreview(taskId) {
|
|
383
|
+
const { taskId: id, state } = stateOf(taskId);
|
|
384
|
+
const review = state.review;
|
|
385
|
+
if (!review || typeof review !== "object" || !review.input) {
|
|
386
|
+
throw new WorkError("no-review", "the run has no review to publish");
|
|
387
|
+
}
|
|
388
|
+
if (!Array.isArray(review.findings)) {
|
|
389
|
+
throw new WorkError("no-findings", "the review has not recorded its findings yet");
|
|
390
|
+
}
|
|
391
|
+
if (!FINISHED.has(state.status)) {
|
|
392
|
+
throw new WorkError("review-incomplete", "the review run has not finished");
|
|
393
|
+
}
|
|
394
|
+
const untriaged = review.findings.filter((f) => !TRIAGED.has(f?.status)).length;
|
|
395
|
+
if (untriaged) {
|
|
396
|
+
throw new WorkError(
|
|
397
|
+
"review-incomplete",
|
|
398
|
+
`${untriaged} finding(s) are not triaged yet (accepted, rejected or deferred)`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const accepted = review.findings.filter((f) => f?.status === "accepted");
|
|
402
|
+
const blocking = accepted.filter((f) => f?.severity === "blocking").length;
|
|
403
|
+
const important = accepted.filter((f) => f?.severity === "important").length;
|
|
404
|
+
return {
|
|
405
|
+
contractVersion: CONTRACT_VERSION,
|
|
406
|
+
taskId: id,
|
|
407
|
+
provider: ["github", "bitbucket-server"].includes(review.input.provider)
|
|
408
|
+
? review.input.provider
|
|
409
|
+
: null,
|
|
410
|
+
prNumber: Number.isInteger(review.input.prNumber) ? review.input.prNumber : null,
|
|
411
|
+
decision: blocking === 0 && important === 0 ? "approve" : "needs-work",
|
|
412
|
+
acceptedBlocking: blocking,
|
|
413
|
+
acceptedImportant: important,
|
|
414
|
+
findings: review.findings.length,
|
|
415
|
+
findingsDigest: findingsDigest(review.findings),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Post the review and its verdict through post-pr-review.sh, as
|
|
421
|
+
* /multi-agent:review does. With `expected` ({decision, findingsDigest} from
|
|
422
|
+
* the preview a person confirmed), refused with `changed` when either differs
|
|
423
|
+
* now.
|
|
424
|
+
*/
|
|
425
|
+
export async function postReviewVerdict(taskId, env = process.env, expected = null) {
|
|
426
|
+
const preview = reviewVerdictPreview(taskId);
|
|
427
|
+
if (
|
|
428
|
+
expected &&
|
|
429
|
+
(expected.decision !== preview.decision || expected.findingsDigest !== preview.findingsDigest)
|
|
430
|
+
) {
|
|
431
|
+
throw new WorkError("changed", "the findings or the decision changed since the preview");
|
|
432
|
+
}
|
|
433
|
+
const script = libScript(env, "post-pr-review.sh", "MA_POST_PR_REVIEW");
|
|
434
|
+
if (!script) throw new WorkError("not-installed", "post-pr-review.sh is not installed");
|
|
435
|
+
const r = await spawnAsync("bash", [script, preview.taskId], {
|
|
436
|
+
env,
|
|
437
|
+
timeoutMs: 5 * TIMEOUT_MS,
|
|
438
|
+
maxBytes: 1024 * 1024,
|
|
439
|
+
});
|
|
440
|
+
if (r.status !== 0) {
|
|
441
|
+
throw new WorkError("post-failed", `post-pr-review.sh exited ${r.status ?? "on a signal"}`);
|
|
442
|
+
}
|
|
443
|
+
return { ...preview, posted: true };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function main(argv) {
|
|
447
|
+
const [cmd, arg, flag] = argv;
|
|
448
|
+
try {
|
|
449
|
+
let out;
|
|
450
|
+
if (cmd === "maturity" && arg) out = await issueMaturity(arg);
|
|
451
|
+
else if (cmd === "prs" && !arg) out = await pullRequests();
|
|
452
|
+
else if (cmd === "verdict" && arg && (flag === undefined || flag === "--post")) {
|
|
453
|
+
out = flag ? await postReviewVerdict(arg) : reviewVerdictPreview(arg);
|
|
454
|
+
} else {
|
|
455
|
+
process.stderr.write(
|
|
456
|
+
"usage: work-items.mjs maturity <key> | prs | verdict <task-id> [--post]\n",
|
|
457
|
+
);
|
|
458
|
+
return 2;
|
|
459
|
+
}
|
|
460
|
+
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
461
|
+
return 0;
|
|
462
|
+
} catch (err) {
|
|
463
|
+
if (!(err instanceof WorkError)) throw err;
|
|
464
|
+
process.stderr.write(`work-items: ${err.message}\n`);
|
|
465
|
+
return err.code === "no-run" ? 3 : 1;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (invokedDirectly(import.meta.url)) {
|
|
470
|
+
runMain("work-items", async () => {
|
|
471
|
+
process.exitCode = await main(process.argv.slice(2));
|
|
472
|
+
});
|
|
473
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-10-
|
|
4
|
-
"skillCount":
|
|
3
|
+
"generatedAt": "2026-10-25T17:09:06Z",
|
|
4
|
+
"skillCount": 237,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"path": "shared/core/apple-archive-compliance/SKILL.md",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "shared/core/multi-agent-autopilot-on/SKILL.md",
|
|
32
|
-
"sha256": "
|
|
32
|
+
"sha256": "7e4fbc84ed2bac8ba5fae1b9384ffbd6a40421a3df7bc1a63ef8f0f7fe6a3e55"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "shared/core/multi-agent-autopilot-status/SKILL.md",
|
|
@@ -127,13 +127,25 @@
|
|
|
127
127
|
"path": "shared/core/multi-agent-model/SKILL.md",
|
|
128
128
|
"sha256": "57ba710657d74176b0f19d4a97d3183df1f7b0c056eb1eef2792308914be836c"
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
"path": "shared/core/multi-agent-office-off/SKILL.md",
|
|
132
|
+
"sha256": "316c7f1cd910e76096301330cac7e02f1a2b28e66cda88e9f8bb966a14553e9e"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"path": "shared/core/multi-agent-office-on/SKILL.md",
|
|
136
|
+
"sha256": "b46a2f79beeee65ae7ad6cbfc58b4631c5146a7761e91a99b8a056d6f49c690d"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"path": "shared/core/multi-agent-office-status/SKILL.md",
|
|
140
|
+
"sha256": "fba0513f8941c026172ea0ce76cbc76d88a5e813fd713d68a1377eb5a4204c86"
|
|
141
|
+
},
|
|
130
142
|
{
|
|
131
143
|
"path": "shared/core/multi-agent-postmortem/SKILL.md",
|
|
132
144
|
"sha256": "41edd907cbaae1523eab1cd716390c9e9cd889574141cb88c33966e221b0987d"
|
|
133
145
|
},
|
|
134
146
|
{
|
|
135
147
|
"path": "shared/core/multi-agent-prune-logs/SKILL.md",
|
|
136
|
-
"sha256": "
|
|
148
|
+
"sha256": "649622e6f18887f986e0acd8b743df0a345a3adf89ccab58d6c4b191b770c1ac"
|
|
137
149
|
},
|
|
138
150
|
{
|
|
139
151
|
"path": "shared/core/multi-agent-prune-prompts/SKILL.md",
|
|
@@ -169,7 +181,7 @@
|
|
|
169
181
|
},
|
|
170
182
|
{
|
|
171
183
|
"path": "shared/core/multi-agent-review/SKILL.md",
|
|
172
|
-
"sha256": "
|
|
184
|
+
"sha256": "4a110e0cc6c36185d76e1a99d7a397a937460377c6bc426714a236a5371a26b0"
|
|
173
185
|
},
|
|
174
186
|
{
|
|
175
187
|
"path": "shared/core/multi-agent-route-off/SKILL.md",
|
|
@@ -201,7 +213,7 @@
|
|
|
201
213
|
},
|
|
202
214
|
{
|
|
203
215
|
"path": "shared/core/multi-agent-scenario-audit/SKILL.md",
|
|
204
|
-
"sha256": "
|
|
216
|
+
"sha256": "b8bc2de0e889007ae64d615e291ea6d369c96dbed0b62fed47cf19c372af4f1b"
|
|
205
217
|
},
|
|
206
218
|
{
|
|
207
219
|
"path": "shared/core/multi-agent-search/SKILL.md",
|
|
@@ -213,7 +225,7 @@
|
|
|
213
225
|
},
|
|
214
226
|
{
|
|
215
227
|
"path": "shared/core/multi-agent-serve/SKILL.md",
|
|
216
|
-
"sha256": "
|
|
228
|
+
"sha256": "82ba766a7b23285f1b2b3eba2bcbd671ade82b28cc094093832075970504f732"
|
|
217
229
|
},
|
|
218
230
|
{
|
|
219
231
|
"path": "shared/core/multi-agent-setup/SKILL.md",
|
|
@@ -221,7 +233,7 @@
|
|
|
221
233
|
},
|
|
222
234
|
{
|
|
223
235
|
"path": "shared/core/multi-agent-stack/SKILL.md",
|
|
224
|
-
"sha256": "
|
|
236
|
+
"sha256": "0e32b95c62324bb592d10b4c2c515509998e7260f6c4aae94271683ede9ba763"
|
|
225
237
|
},
|
|
226
238
|
{
|
|
227
239
|
"path": "shared/core/multi-agent-status/SKILL.md",
|
|
@@ -229,7 +241,7 @@
|
|
|
229
241
|
},
|
|
230
242
|
{
|
|
231
243
|
"path": "shared/core/multi-agent-steer/SKILL.md",
|
|
232
|
-
"sha256": "
|
|
244
|
+
"sha256": "9ac0c48c3cbafd98022a04b24ef4f16723dda6a852202002231f43bbb7dd453b"
|
|
233
245
|
},
|
|
234
246
|
{
|
|
235
247
|
"path": "shared/core/multi-agent-store-ready/SKILL.md",
|
|
@@ -237,7 +249,7 @@
|
|
|
237
249
|
},
|
|
238
250
|
{
|
|
239
251
|
"path": "shared/core/multi-agent-sync/SKILL.md",
|
|
240
|
-
"sha256": "
|
|
252
|
+
"sha256": "17f734fa2b84d9a4c757f9745be8ff40d3097f5de89caf55124b48439a737093"
|
|
241
253
|
},
|
|
242
254
|
{
|
|
243
255
|
"path": "shared/core/multi-agent-test-accessibility/SKILL.md",
|
|
@@ -253,7 +265,7 @@
|
|
|
253
265
|
},
|
|
254
266
|
{
|
|
255
267
|
"path": "shared/core/multi-agent-test-gen/SKILL.md",
|
|
256
|
-
"sha256": "
|
|
268
|
+
"sha256": "a66d1171455d95df92a367095eeec11f50033d86c53a7d0032597cebd40d6f21"
|
|
257
269
|
},
|
|
258
270
|
{
|
|
259
271
|
"path": "shared/core/multi-agent-test-screenshots/SKILL.md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"skillCount":
|
|
3
|
+
"skillCount": 237,
|
|
4
4
|
"entries": [
|
|
5
5
|
{
|
|
6
6
|
"name": "accessibility-compliance-accessibility-audit",
|
|
@@ -1355,6 +1355,39 @@
|
|
|
1355
1355
|
"triggerPaths": [],
|
|
1356
1356
|
"relativePath": "shared/core/multi-agent-model/SKILL.md"
|
|
1357
1357
|
},
|
|
1358
|
+
{
|
|
1359
|
+
"name": "multi-agent-office-off",
|
|
1360
|
+
"description": "Close the office panel and stop its local server. Use when done with the panel.",
|
|
1361
|
+
"platform": null,
|
|
1362
|
+
"group": "core",
|
|
1363
|
+
"plugin": null,
|
|
1364
|
+
"invokeAs": "multi-agent-office-off",
|
|
1365
|
+
"triggerKeywords": [],
|
|
1366
|
+
"triggerPaths": [],
|
|
1367
|
+
"relativePath": "shared/core/multi-agent-office-off/SKILL.md"
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
"name": "multi-agent-office-on",
|
|
1371
|
+
"description": "Open the office: a local browser page that shows runs and agents live and drives the ecosystem (new runs, jobs, PRs, keys, memory, system, cleanup). Use when you want to watch or drive runs from a browser.",
|
|
1372
|
+
"platform": null,
|
|
1373
|
+
"group": "core",
|
|
1374
|
+
"plugin": null,
|
|
1375
|
+
"invokeAs": "multi-agent-office-on",
|
|
1376
|
+
"triggerKeywords": [],
|
|
1377
|
+
"triggerPaths": [],
|
|
1378
|
+
"relativePath": "shared/core/multi-agent-office-on/SKILL.md"
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
"name": "multi-agent-office-status",
|
|
1382
|
+
"description": "Whether the office panel is running, its URL (never the token) and its last actions. Use when unsure if the panel is open.",
|
|
1383
|
+
"platform": null,
|
|
1384
|
+
"group": "core",
|
|
1385
|
+
"plugin": null,
|
|
1386
|
+
"invokeAs": "multi-agent-office-status",
|
|
1387
|
+
"triggerKeywords": [],
|
|
1388
|
+
"triggerPaths": [],
|
|
1389
|
+
"relativePath": "shared/core/multi-agent-office-status/SKILL.md"
|
|
1390
|
+
},
|
|
1358
1391
|
{
|
|
1359
1392
|
"name": "multi-agent-postmortem",
|
|
1360
1393
|
"description": "Post-mortem of a run from its session transcripts: loops, stalls, root-cause hypotheses on file:line, fixes. Read-only, redacted. Use when a run went wrong or ran far too long.",
|
|
@@ -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:** 237 skills (72 core + 165 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/`** - 72 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
|
|
10
10
|
- **`external/`** - 165 iOS / Android / generic skills that ship into the stack plugins. Each names its origin in `metadata.source`: `multi-agent-pipeline` when written here, or the upstream and license a `NOTICE-*.md` in this directory covers.
|
|
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) - 72
|
|
18
18
|
- [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 94
|
|
19
19
|
- [Android / Kotlin](#android-kotlin) - 16
|
|
20
20
|
- [Web](#web) - 10
|
|
@@ -57,6 +57,9 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
57
57
|
| [`multi-agent-log`](./core/multi-agent-log/) | `core` | Show the agent-log.md for the given task. With no ID, shows the most recent task. Use when asked what a task did, or to read its log. |
|
|
58
58
|
| [`multi-agent-manual-test`](./core/multi-agent-manual-test/) | `core` | Switch to the active task's branch and prepare it for manual testing in Xcode. Phase 3 user-test step, standalone (the UI Bug Hunter lives a |
|
|
59
59
|
| [`multi-agent-model`](./core/multi-agent-model/) | `core` | Turn the top model rung on or off and keep the cost ledger's pricing in step with it. Use when asked to enable or disable Fable, or which mo |
|
|
60
|
+
| [`multi-agent-office-off`](./core/multi-agent-office-off/) | `core` | Close the office panel and stop its local server. Use when done with the panel. |
|
|
61
|
+
| [`multi-agent-office-on`](./core/multi-agent-office-on/) | `core` | Open the office: a local browser page that shows runs and agents live and drives the ecosystem (new runs, jobs, PRs, keys, memory, system, c |
|
|
62
|
+
| [`multi-agent-office-status`](./core/multi-agent-office-status/) | `core` | Whether the office panel is running, its URL (never the token) and its last actions. Use when unsure if the panel is open. |
|
|
60
63
|
| [`multi-agent-postmortem`](./core/multi-agent-postmortem/) | `core` | Post-mortem of a run from its session transcripts: loops, stalls, root-cause hypotheses on file:line, fixes. Read-only, redacted. Use when a |
|
|
61
64
|
| [`multi-agent-prune-logs`](./core/multi-agent-prune-logs/) | `core` | Delete per-task project logs under ~/.claude/logs/multi-agent (filter by age/project/task). Audit trail + metrics are preserved. Dry-run fir |
|
|
62
65
|
| [`multi-agent-prune-prompts`](./core/multi-agent-prune-prompts/) | `core` | Zero-base prompt review: measure the always-on instruction footprint, classify every rule block, propose keep/trial-removal/delete; applies |
|