@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
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,133 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## [18.0.0] - 2026-09-17
|
|
18
|
+
|
|
19
|
+
Major, for two behaviour changes rather than a renamed command: a run's state now
|
|
20
|
+
has ONE canonical directory, and four publish paths refuse to send when the leak
|
|
21
|
+
gate cannot be loaded.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`multi-agent-pipeline verify`** answers "is this install the thing that was
|
|
26
|
+
published". The install is a copy, and from the moment `install.js` writes it
|
|
27
|
+
the two halves drift independently: an edit in the installed tree is behaviour
|
|
28
|
+
with no source, and a file the installer skipped is a script the docs describe
|
|
29
|
+
and nobody has. `manifest.json` (SHA-256 per shipped file, version, source
|
|
30
|
+
commit) is built at pack time by `prepack` and never committed - a manifest in
|
|
31
|
+
git is stale one commit after it is written, and a stale manifest reports
|
|
32
|
+
honest edits as tampering.
|
|
33
|
+
|
|
34
|
+
`commands/` is compared by presence, not bytes, because `install.js` rewrites
|
|
35
|
+
each description into the user's `outputLanguage`: measured here, all 57
|
|
36
|
+
command files differ and 56 of them differ by nothing else. Dev-only files are
|
|
37
|
+
excluded, or 252 smokes and linters read as "the installer skipped this".
|
|
38
|
+
|
|
39
|
+
What a green result proves is stated in the output's own reference: the bytes
|
|
40
|
+
match what the publisher recorded. Not who published them - the manifest, the
|
|
41
|
+
signature and the verifier travel in the same tarball, so provenance belongs
|
|
42
|
+
to npm's integrity field. Signing is optional, and an unverifiable signature
|
|
43
|
+
says so rather than claiming valid.
|
|
44
|
+
|
|
45
|
+
- **`cost-analyze.mjs`** - projection, anomaly, burn and diff. `cost-budget-check`
|
|
46
|
+
watches one run against one ceiling, which is blind to both ways a budget
|
|
47
|
+
actually empties: a drift no single run trips, and one session that burns a
|
|
48
|
+
week in an hour while every run stays under its cap.
|
|
49
|
+
|
|
50
|
+
The series is not where the schema says it is. `tracker-state.json` carries
|
|
51
|
+
per-phase token fields that are written only when a phase reports them; of 98
|
|
52
|
+
trackers on a working machine, zero carry any. The dense series is the host's
|
|
53
|
+
own transcripts, so that is what this reads - and the consequences are printed
|
|
54
|
+
rather than buried: it covers everything Claude Code did on the machine, the
|
|
55
|
+
figures are LIST-price estimates rather than a bill, and a host with no
|
|
56
|
+
transcripts reports UNMEASURED instead of zero.
|
|
57
|
+
|
|
58
|
+
Anomalies use the median and the MAD, because the expensive session the check
|
|
59
|
+
exists to find is the observation that inflates a mean and a standard
|
|
60
|
+
deviation - it hides inside the statistic measured against it.
|
|
61
|
+
|
|
62
|
+
- **`install --unattended`** writes a documented permission profile, and prints
|
|
63
|
+
it with a reason per line before writing. autopilot passes
|
|
64
|
+
`--permission-prompts none`, which stops Claude Code asking and grants
|
|
65
|
+
nothing; on a fresh machine the run stops at the first tool call with no
|
|
66
|
+
prompt for anyone to answer, which is indistinguishable from an empty queue. A
|
|
67
|
+
default install still writes no permissions at all.
|
|
68
|
+
|
|
69
|
+
- **`doctor --profile=server`** adds four checks that only matter when nobody is
|
|
70
|
+
at the keyboard: the unattended contract, the permission posture, the
|
|
71
|
+
scheduler, and the keychain. The default run is unchanged - same 17 checks,
|
|
72
|
+
same verdict - because a laptop told it fails a server check learns to ignore
|
|
73
|
+
doctor.
|
|
74
|
+
|
|
75
|
+
- **`docs/server-readiness.md`** sets up nothing and says so first. It covers the
|
|
76
|
+
permission posture, why the scheduler is a LaunchAgent rather than a
|
|
77
|
+
LaunchDaemon (the keychain is locked until login, and that failure arrives
|
|
78
|
+
much later wearing a 401), and which credential each phase needs.
|
|
79
|
+
|
|
80
|
+
- **`scorecard-snapshot.mjs --diff`** keeps what the scorecard said and reports
|
|
81
|
+
what moved. Deliberately no 0-100 score: the scorecard reports twelve measured
|
|
82
|
+
metrics AND four it refuses to measure, and one figure would hide both halves.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
|
|
86
|
+
- **A run's state has one canonical directory.** `{project}/{taskId}/` is
|
|
87
|
+
canonical and the flat `{taskId}/` is read for compatibility; every reader
|
|
88
|
+
resolves through `lib/run-paths.sh` / `scripts/_run-paths.mjs`, so a run that
|
|
89
|
+
exists in both layouts is counted once.
|
|
90
|
+
|
|
91
|
+
- **Five publish paths refuse rather than send when the leak gate is missing.**
|
|
92
|
+
Jira comments and descriptions, PR review bodies and inline comments, the
|
|
93
|
+
GitHub issue progress comment, the issue-CREATION path, and Confluence page
|
|
94
|
+
create/update all run their outbound text through `lib/outbound-gate.mjs`
|
|
95
|
+
first. A missing gate file refuses; opening the gate because the gate is not
|
|
96
|
+
there would be the one failure mode that matters. The count is part of the
|
|
97
|
+
gate: a sixth publisher cannot ship without the smoke's list naming it.
|
|
98
|
+
|
|
99
|
+
- **`set -e` on the four destructive scripts, and deliberately not on the two
|
|
100
|
+
collectors.** `gc-abandoned`, `gc-worktrees`, `purge` and `worktree-finalize`
|
|
101
|
+
delete things, so the command after an unnoticed failure is the dangerous one.
|
|
102
|
+
`pre-push-check` runs the gates and counts failures, and `pre-commit-check` is
|
|
103
|
+
a hook built out of greps that are supposed to find nothing - under `-e` the
|
|
104
|
+
first clean detector would end the scan and report "no secrets" for a file it
|
|
105
|
+
never finished reading.
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- **A script that dies now prints one line instead of a stack dump, and releases
|
|
110
|
+
what it held.** `lib/fatal.mjs` catches the sync throw, the rejection nobody
|
|
111
|
+
awaited and the throw from inside a callback; the last two are invisible to a
|
|
112
|
+
try/catch around `main()`. `write-state` releases its advisory lock on the way
|
|
113
|
+
out, so the next writer never has to judge a lock on age alone. EPIPE is
|
|
114
|
+
deliberately not fatal: `runs-index.mjs --json | head` is the ordinary way to
|
|
115
|
+
read a large output.
|
|
116
|
+
|
|
117
|
+
- **Build junk no longer reaches an install.** A CI runner shipped 147 files
|
|
118
|
+
where every developer tree shipped 146, for four rounds, and the extra was
|
|
119
|
+
`__pycache__/*.pyc` - untracked, so no diff of the source could show it, and
|
|
120
|
+
copied verbatim into all three install trees. `copyDir` now filters
|
|
121
|
+
`__pycache__`, `*.pyc` and `.DS_Store` on every path.
|
|
122
|
+
|
|
123
|
+
- **`jq` is required rather than optional on the nine paths that publish or
|
|
124
|
+
decide.** A missing `jq` renders as empty DATA and the work carries on with
|
|
125
|
+
it; those nine now exit 3.
|
|
126
|
+
|
|
127
|
+
- **The autopilot runner survives a month unwatched.** `runner.log` is truncated
|
|
128
|
+
in place past 5MB (renaming it leaves launchd's `O_APPEND` descriptor writing
|
|
129
|
+
into the renamed file while the new one stays empty - the rotation that looks
|
|
130
|
+
right and silently stops logging), three consecutive empty attempts stop new
|
|
131
|
+
work being taken, and one JSON line per tick goes to `ticks.jsonl`.
|
|
132
|
+
|
|
133
|
+
- **`github-ssh-setup.sh` no longer waits forever on a headless machine**, and
|
|
134
|
+
`write-state.mjs` gained `--if-rev=<n>` compare-and-swap so a second writer
|
|
135
|
+
cannot silently overwrite the first.
|
|
136
|
+
|
|
137
|
+
- **A direct-run guard that compared `import.meta.url` to `file://${argv[1]}`**
|
|
138
|
+
is false whenever argv[1] is not already resolved - a `/var` path that
|
|
139
|
+
resolves to `/private/var`, or the symlink `install --link` writes. The script
|
|
140
|
+
then does nothing at all, silently.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
17
144
|
## [17.6.0] - 2026-09-15
|
|
18
145
|
|
|
19
146
|
### Added
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Runs natively on Claude Code, Copilot CLI and Codex CLI. macOS only. Zero runtim
|
|
|
17
17
|
### Prerequisites
|
|
18
18
|
|
|
19
19
|
- **Node.js >= 20.11** - required; the pipeline's own tooling runs on it.
|
|
20
|
-
- **`jq`** - optional
|
|
20
|
+
- **`jq`** - required for nine paths, optional for the rest. 78 shell files call it. The nine that publish or decide - the autopilot queue, Jira comments, PR reviews, issue updates, the plan file, both Figma fetchers, log search and Jira auth - now refuse with exit 3 rather than run, because a missing `jq` renders as empty DATA and the work carries on with it. Everywhere else it still degrades. The install prints a note when it is missing.
|
|
21
21
|
- **`gh`** - for GitHub issue and PR work. Its built-in `--jq` is independent of the `jq` binary.
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
@@ -33,6 +33,41 @@ npx @mmerterden/multi-agent-pipeline install --all # all three
|
|
|
33
33
|
/multi-agent:setup # keychain token scan + git identity + default stack
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### No `npx` on that machine?
|
|
37
|
+
|
|
38
|
+
`npx` ships with npm, and npm ships with Node - so "npx: command not found" almost
|
|
39
|
+
always means Node is missing from that shell, not that anything is wrong with the
|
|
40
|
+
package. Check first, then pick the row that matches:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
node -v; npm -v; command -v node npm npx
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| What you see | What to do |
|
|
47
|
+
|---|---|
|
|
48
|
+
| nothing at all | Install Node >= 20.11: `brew install node`, or the LTS installer from nodejs.org |
|
|
49
|
+
| `node` works, `npx` does not | `npm i -g @mmerterden/multi-agent-pipeline` then `multi-agent-pipeline install --claude` |
|
|
50
|
+
| nvm is installed but the shell does not see it | `source ~/.nvm/nvm.sh && nvm use --lts`, or just open a new terminal |
|
|
51
|
+
| npm is ancient (< 5.2, which predates npx) | `npm i -g npm@latest`, or use the global-install row above |
|
|
52
|
+
|
|
53
|
+
And the path that needs neither `npx` nor a global install - clone and run the
|
|
54
|
+
installer directly:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/mmerterden/multi-agent-pipeline.git
|
|
58
|
+
cd multi-agent-pipeline
|
|
59
|
+
node index.js install --claude # add --dry-run first to see what it would write
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`npm exec @mmerterden/multi-agent-pipeline install --claude` also works on any npm
|
|
63
|
+
7+ without `npx` on `PATH`.
|
|
64
|
+
|
|
65
|
+
**One error that is not an npx problem.** This package declares `os: ["darwin"]`,
|
|
66
|
+
so npm refuses to install it anywhere else and says `npm ERR! notsup Unsupported
|
|
67
|
+
platform`. That is deliberate ([ADR-0012](./docs/adr/0012-macos-only.md)), not a
|
|
68
|
+
missing tool: every credential read shells `security`, every iOS build
|
|
69
|
+
`xcodebuild`, every piece of visual evidence `simctl`.
|
|
70
|
+
|
|
36
71
|
Tool flags combine (`--claude --codex`). With no tool flag at all, the installer targets Claude Code only. Other flags: `--dry-run` (show what would be written, write nothing), `--platform=ios|android|all` (skip the stack skills you do not need), `--link` (symlink instead of copy, for local development).
|
|
37
72
|
|
|
38
73
|
Run a task - the input type is auto-detected:
|
|
@@ -72,6 +107,13 @@ checkout):
|
|
|
72
107
|
|
|
73
108
|
`/multi-agent:analysis` runs its own shorter chain and, since v16.12.0, reviews what it wrote before publishing it: the draft goes through the same three-reviewer set and triage as a code diff, a blocking finding returns it to synthesis with dispatch closed, and the gaps that survive are either searched, asked about, or recorded with an owner. It used to publish behind a structural validator alone.
|
|
74
109
|
|
|
110
|
+
### 18.0.0: one state directory, and a way to ask whether your install is real
|
|
111
|
+
|
|
112
|
+
- **`multi-agent-pipeline verify`.** The install is a copy, and from the moment it is written the two halves drift independently: an edit in the installed tree is behaviour with no source, and a file the installer skipped is a script the docs describe and nobody has. `verify` compares both against a manifest built at pack time. What a green result proves is stated plainly - the bytes match what the publisher recorded, not who published them.
|
|
113
|
+
- **Cost, past the single run.** The per-task ceiling cannot see the two ways a budget actually empties: a drift that trips nothing, and one session that burns a week in an hour while every run stays under its cap. `cost-analyze` projects, finds days out of family by median absolute deviation, and reports acceleration - as LIST-price estimates, which it says on every run rather than in a footnote.
|
|
114
|
+
- **A run's state lives in one place.** `{project}/{taskId}/` is canonical, the flat layout is still read, and a run that exists in both is counted once.
|
|
115
|
+
- **Server readiness, entirely opt-in.** `doctor --profile=server` adds four checks that only matter when nobody is at the keyboard, `install --unattended` writes a permission profile after printing it, and the autopilot runner now survives a month unwatched. The default install writes no permissions and the default doctor run is unchanged, because a laptop told it fails a server check learns to ignore doctor.
|
|
116
|
+
|
|
75
117
|
### Your package manager, your hooks, your MCP surface
|
|
76
118
|
|
|
77
119
|
Three smaller things in 17.6.0, each closing a gap where the pipeline assumed instead of looking:
|
package/README.tr.md
CHANGED
|
@@ -33,6 +33,40 @@ npx @mmerterden/multi-agent-pipeline install --all # üçü birden
|
|
|
33
33
|
/multi-agent:setup # keychain token taraması + git kimliği + varsayılan stack
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### O makinede `npx` yoksa
|
|
37
|
+
|
|
38
|
+
`npx` npm ile, npm de Node ile gelir - yani "npx: command not found" neredeyse her
|
|
39
|
+
zaman o kabukta Node olmadığı anlamına gelir, pakette bir sorun olduğu değil. Önce
|
|
40
|
+
bak, sonra sana uyan satırı uygula:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
node -v; npm -v; command -v node npm npx
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Gördüğün | Yapılacak |
|
|
47
|
+
|---|---|
|
|
48
|
+
| hiçbiri yok | Node >= 20.11 kur: `brew install node` ya da nodejs.org'dan LTS installer |
|
|
49
|
+
| `node` çalışıyor, `npx` çalışmıyor | `npm i -g @mmerterden/multi-agent-pipeline` sonra `multi-agent-pipeline install --claude` |
|
|
50
|
+
| nvm kurulu ama kabuk görmüyor | `source ~/.nvm/nvm.sh && nvm use --lts`, ya da yeni bir terminal aç |
|
|
51
|
+
| npm çok eski (< 5.2, npx'ten önceki sürümler) | `npm i -g npm@latest`, ya da üstteki global kurulum satırı |
|
|
52
|
+
|
|
53
|
+
Ne `npx` ne de global kurulum isteyen yol - klonla ve installer'ı doğrudan çalıştır:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/mmerterden/multi-agent-pipeline.git
|
|
57
|
+
cd multi-agent-pipeline
|
|
58
|
+
node index.js install --claude # önce --dry-run ile ne yazacağını görebilirsin
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`npm exec @mmerterden/multi-agent-pipeline install --claude` de, `PATH`'te `npx`
|
|
62
|
+
olmayan her npm 7+ üzerinde çalışır.
|
|
63
|
+
|
|
64
|
+
**npx sorunu olmayan bir hata.** Bu paket `os: ["darwin"]` beyan eder; npm başka
|
|
65
|
+
hiçbir yerde kurmaz ve `npm ERR! notsup Unsupported platform` der. Bu kasıtlıdır
|
|
66
|
+
([ADR-0012](./docs/adr/0012-macos-only.md)), eksik bir araç değil: her credential
|
|
67
|
+
okuması `security`, her iOS build'i `xcodebuild`, her görsel kanıt `simctl`
|
|
68
|
+
çağırıyor.
|
|
69
|
+
|
|
36
70
|
Tool flag'leri birleştirilebilir (`--claude --codex`). Hiç tool flag'i verilmezse installer sadece Claude Code'u hedefler. Diğer flag'ler: `--dry-run` (ne yazılacağını gösterir, hiçbir şey yazmaz), `--platform=ios|android|all` (ihtiyacın olmayan stack skill'lerini atlar), `--link` (kopyalamak yerine symlink, lokal geliştirme için).
|
|
37
71
|
|
|
38
72
|
Bir görev çalıştır - girdi tipi otomatik algılanır:
|
|
@@ -72,6 +106,13 @@ mu):
|
|
|
72
106
|
|
|
73
107
|
`/multi-agent:analysis` kendi kısa zincirini koşar ve v16.12.0'dan beri yazdığını yayınlamadan önce review ediyor: taslak, bir kod diff'iyle aynı üç-reviewer setinden ve triyajdan geçiyor, bloklayıcı bulgu dokümanı sentez fazına geri gönderip dispatch'i kapatıyor, hayatta kalan boşluklar ya aranıyor ya sana soruluyor ya da sahibiyle birlikte kayda giriyor. Önceden yalnızca yapısal bir validator'ın arkasından yayınlıyordu.
|
|
74
108
|
|
|
109
|
+
### 18.0.0: tek bir durum dizini, ve kurulumun gerçekten o kurulum olup olmadığını sorma yolu
|
|
110
|
+
|
|
111
|
+
- **`multi-agent-pipeline verify`.** Kurulum bir kopyadır ve yazıldığı andan itibaren iki taraf birbirinden bağımsız kayar: kurulu ağaçtaki bir düzenleme kaynağı olmayan bir davranıştır, kurulumun atladığı dosya ise dokümanın anlattığı ama kimsede olmayan bir script. `verify` ikisini de paketleme anında üretilen bir manifest'e karşı karşılaştırır. Yeşil sonucun ne kanıtladığı açıkça yazılıdır: baytlar yayıncının kaydettiğiyle aynıdır, kimin yayınladığı değil.
|
|
112
|
+
- **Maliyet, tek koşunun ötesinde.** Görev başına tavan, bütçeyi gerçekten bitiren iki şeyi göremez: hiçbir koşuyu aşmayan kayma, ve her koşu tavanın altında kalırken bir saatte bir haftayı yakan tek oturum. `cost-analyze` projeksiyon yapar, medyan mutlak sapmayla aileden ayrılan günü bulur ve ivmeyi raporlar - liste fiyatından tahmin olarak, ve bunu dipnotta değil her koşuda söyler.
|
|
113
|
+
- **Bir koşunun durumu tek yerde.** `{project}/{taskId}/` kanonik, düz yerleşim hâlâ okunuyor, ve iki yerde birden duran koşu bir kez sayılıyor.
|
|
114
|
+
- **Sunucu hazırlığı, tamamen opt-in.** `doctor --profile=server` yalnızca klavyede kimse yokken anlamı olan dört kontrol ekler, `install --unattended` izin profilini yazmadan önce gösterir, autopilot runner artık bir ay gözetimsiz ayakta kalır. Varsayılan kurulum hiçbir izin yazmaz ve varsayılan doctor koşusu değişmez: sunucu kontrolünden kaldığı söylenen bir dizüstü, doctor'ı görmezden gelmeyi öğrenir.
|
|
115
|
+
|
|
75
116
|
### Paket yöneticisi, hook'lar ve MCP yüzeyi
|
|
76
117
|
|
|
77
118
|
17.6.0'da üç küçük iş; üçü de pipeline'ın bakmak yerine varsaydığı bir yeri kapatıyor:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 11. CI stays in the repository but dormant; the pre-push gate is primary
|
|
2
2
|
|
|
3
|
-
**Status:** Accepted · 2026-08-31 · amended 2026-09-13
|
|
3
|
+
**Status:** Accepted · 2026-08-31 · amended 2026-09-13 · amended 2026-09-17
|
|
4
4
|
|
|
5
5
|
> **Amendment, 2026-09-13 (ADR-0012, macOS only).** Linux and Windows support
|
|
6
6
|
> was removed. `package.json` now declares `os: ["darwin"]`, which npm enforces
|
|
@@ -91,6 +91,30 @@ gate already covers everything except the three items listed above.
|
|
|
91
91
|
only macOS, disappears when the laptop is closed, and a self-hosted runner on a
|
|
92
92
|
public repository executes code from any fork's pull request.
|
|
93
93
|
|
|
94
|
+
> **Amendment, 2026-09-17.** Two of those three reasons have moved.
|
|
95
|
+
>
|
|
96
|
+
> "Covers only macOS" stopped being a cost when ADR-0012 removed the other
|
|
97
|
+
> platforms: macOS is now the only target, so a macOS-only runner covers all of
|
|
98
|
+
> it.
|
|
99
|
+
>
|
|
100
|
+
> The fork argument is weaker than it was but is NOT gone, and the difference is
|
|
101
|
+
> worth stating precisely because the plan that revisited this got it wrong.
|
|
102
|
+
> The repository is private, which is the important half. It is not
|
|
103
|
+
> fork-free: measured on 2026-09-17 it has **one fork**, owned by another
|
|
104
|
+
> account. GitHub requires approval before a fork's pull request runs a
|
|
105
|
+
> workflow, so the exposure is gated - but the gate is a setting and an
|
|
106
|
+
> approval given by reflex puts that code on the runner, with the runner's
|
|
107
|
+
> keychain.
|
|
108
|
+
>
|
|
109
|
+
> "Disappears when the laptop is closed" is the reason this is still not the
|
|
110
|
+
> default. It becomes a real option only on a machine that stays awake, which
|
|
111
|
+
> is what `docs/server-readiness.md` describes and why that page is a
|
|
112
|
+
> prerequisite rather than a companion.
|
|
113
|
+
>
|
|
114
|
+
> The decision is unchanged: CI stays dormant and the pre-push gate stays
|
|
115
|
+
> primary. What changed is that a self-hosted runner is now a supported option
|
|
116
|
+
> for someone who has read that page, rather than a rejected one.
|
|
117
|
+
|
|
94
118
|
**Delete the workflows.** Honest but lossy. The files encode which steps matter
|
|
95
119
|
and in what order, and the possible issue-triggered direction would have to
|
|
96
120
|
rebuild them from nothing.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Running multi-agent on a machine nobody is sitting at
|
|
2
|
+
|
|
3
|
+
This describes what a Mac has to have before an unattended run works, and what
|
|
4
|
+
breaks if it does not. **Nothing here is set up by reading it** - `install`
|
|
5
|
+
still writes nothing about permissions and no scheduler is loaded unless you
|
|
6
|
+
ask for one. That is deliberate: every item below changes what runs on a
|
|
7
|
+
machine without a human, and none of it should happen because someone ran an
|
|
8
|
+
installer.
|
|
9
|
+
|
|
10
|
+
Scope is macOS. Linux and Windows were removed on purpose (ADR-0012) and
|
|
11
|
+
nothing here reintroduces them.
|
|
12
|
+
|
|
13
|
+
The mechanical half of this page is `doctor --profile=server`, which checks
|
|
14
|
+
four of the conditions below and says which are missing. Read that first; this
|
|
15
|
+
page explains why each one matters and what to do about it.
|
|
16
|
+
|
|
17
|
+
## The failure this page exists for
|
|
18
|
+
|
|
19
|
+
An unattended run does not fail loudly. It **stops without saying so**, and
|
|
20
|
+
every way it does that looks identical from outside: a process that is running,
|
|
21
|
+
printing nothing, finishing never.
|
|
22
|
+
|
|
23
|
+
Four causes, in the order they bite.
|
|
24
|
+
|
|
25
|
+
## 1. The permission posture
|
|
26
|
+
|
|
27
|
+
autopilot spawns its child with `--permission-prompts none`. That stops Claude
|
|
28
|
+
Code from ASKING, and it does not grant anything - the tools the child then
|
|
29
|
+
calls still have to be allowed. On the maintainer's machine they are, because
|
|
30
|
+
`Bash`, `Edit`, `Write` and `Agent` sit in the global allow list from years of
|
|
31
|
+
interactive use. On a fresh machine they do not, and the first tool call ends
|
|
32
|
+
the run.
|
|
33
|
+
|
|
34
|
+
There is no DEFAULT that fixes this and should not be: writing a permission
|
|
35
|
+
posture into someone's settings during an install is the one action an
|
|
36
|
+
installer must never take silently. There is now a flag that asks for it:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx @mmerterden/multi-agent-pipeline install --unattended --dry-run # see it first
|
|
40
|
+
npx @mmerterden/multi-agent-pipeline install --unattended # write it
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
It prints the whole profile with a reason per line before writing anything, and
|
|
44
|
+
it is additive: an entry you added by hand survives, a narrower `Bash(...)` rule
|
|
45
|
+
is kept alongside, unrelated settings are untouched, and a second run changes
|
|
46
|
+
nothing. A `settings.json` that does not parse is refused rather than
|
|
47
|
+
overwritten.
|
|
48
|
+
|
|
49
|
+
By hand, the same thing is:
|
|
50
|
+
|
|
51
|
+
```jsonc
|
|
52
|
+
// ~/.claude/settings.json
|
|
53
|
+
{
|
|
54
|
+
"permissions": {
|
|
55
|
+
"allow": ["Bash", "Edit", "Write", "Agent"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Grant the narrowest set the work needs. A server that only reviews does not
|
|
61
|
+
need `Write` - though note that the shipped profile is deliberately broad,
|
|
62
|
+
because a run builds and tests whatever the target repo uses and a narrow list
|
|
63
|
+
is the one the first unfamiliar repo stops at.
|
|
64
|
+
|
|
65
|
+
`doctor --profile=server` reports this as `unattended-permissions`, against the
|
|
66
|
+
same rule the writer applies.
|
|
67
|
+
|
|
68
|
+
## 2. The scheduler, and why it is a LaunchAgent
|
|
69
|
+
|
|
70
|
+
Something has to start the work. The template ships as a **LaunchAgent**
|
|
71
|
+
(`install/templates/multi-agent-autopilot.plist.template`), loaded in the
|
|
72
|
+
user's session at login - not a LaunchDaemon at boot.
|
|
73
|
+
|
|
74
|
+
That is a keychain decision, not a preference. Every credential the pipeline
|
|
75
|
+
reads lives in the login keychain, and the login keychain is **locked until the
|
|
76
|
+
user logs in**. A daemon started at boot gets a locked one: each credential
|
|
77
|
+
read fails, and those failures surface much later as 401s that blame the token.
|
|
78
|
+
The lock is the cause and nothing in the error says so.
|
|
79
|
+
|
|
80
|
+
So the machine needs to reach a logged-in session by itself:
|
|
81
|
+
|
|
82
|
+
- **System Settings → Users & Groups → Automatic login**, for the account that
|
|
83
|
+
owns the agent.
|
|
84
|
+
- A **keychain with no lock timeout** for that account, or the agent survives
|
|
85
|
+
a login and dies at the first idle period:
|
|
86
|
+
```
|
|
87
|
+
security set-keychain-settings -l ~/Library/Keychains/login.keychain-db
|
|
88
|
+
```
|
|
89
|
+
(no `-t`, so there is no timeout; `-l` still locks on sleep.)
|
|
90
|
+
|
|
91
|
+
Automatic login means physical access to the machine is access to the
|
|
92
|
+
credentials. On a server in a locked room that is the trade being made; on a
|
|
93
|
+
laptop it is not.
|
|
94
|
+
|
|
95
|
+
`doctor --profile=server` reports these as `scheduler` and `keychain-unlock`.
|
|
96
|
+
|
|
97
|
+
## 3. The unattended contract
|
|
98
|
+
|
|
99
|
+
`MULTI_AGENT_UNATTENDED=1` is how a run states that nobody is watching.
|
|
100
|
+
`multi-agent-refs/unattended-contract.md` lists exactly which entry points read
|
|
101
|
+
it and what each resolves to - four of them, named, because a claim of coverage
|
|
102
|
+
that is not true is worse than a short list.
|
|
103
|
+
|
|
104
|
+
The variable does **not** grant permissions and does not suppress errors. It
|
|
105
|
+
only stops a process from waiting for an answer that is not coming.
|
|
106
|
+
|
|
107
|
+
`doctor --profile=server` reports this as `unattended-contract`.
|
|
108
|
+
|
|
109
|
+
## 4. Which credential each phase needs
|
|
110
|
+
|
|
111
|
+
A server fails differently from a laptop here: an interactive run asks for a
|
|
112
|
+
missing token, an unattended one does not.
|
|
113
|
+
|
|
114
|
+
| Phase | Needs | Absent |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| 0 Init | `github` (issue intake), `jira` (ticket intake) | the run cannot resolve its input and stops at the picker |
|
|
117
|
+
| 1-2 Analysis, Plan | `figma` or `figma_mcp` when the task names a design | halts by contract rather than guessing at layout |
|
|
118
|
+
| 3 Dev | none beyond git access | - |
|
|
119
|
+
| 4 Review | `github` for PR comments | findings are computed and never posted |
|
|
120
|
+
| 5 Test | none | - |
|
|
121
|
+
| 6 Commit | git push credential | the branch stays local, which reads as "no PR yet" |
|
|
122
|
+
| 7 Report | `jira`, `confluence` as configured | the report is composed and dropped |
|
|
123
|
+
|
|
124
|
+
`credential-store.sh doctor` lists what is mapped. `doctor --probe` additionally
|
|
125
|
+
checks each one is alive, which costs network calls and is why it is opt-in.
|
|
126
|
+
|
|
127
|
+
## 4b. What the runner does when nobody is looking
|
|
128
|
+
|
|
129
|
+
Three behaviours that are invisible on a laptop and decide whether a server
|
|
130
|
+
survives a month.
|
|
131
|
+
|
|
132
|
+
**The log.** launchd appends the runner's stdout to `runner.log` forever. Each
|
|
133
|
+
tick truncates it past `MA_AP_LOG_MAX_BYTES` (5MB by default), keeping the last
|
|
134
|
+
256KB in `runner.log.1`. It truncates the SAME file rather than renaming it,
|
|
135
|
+
because launchd holds it open with `O_APPEND` - rename it and that descriptor
|
|
136
|
+
keeps writing into the renamed file while the new one stays empty, which is the
|
|
137
|
+
rotation that looks correct and silently stops logging.
|
|
138
|
+
|
|
139
|
+
**The breaker.** An expired token, a `claude` that no longer launches, a full
|
|
140
|
+
disk: every queued item fails the same way, minutes apart, until the queue is
|
|
141
|
+
empty and the ledger is a list of identical failures with no indication which
|
|
142
|
+
came first. After `MA_AP_BREAKER_LIMIT` consecutive attempts that produced
|
|
143
|
+
nothing (3 by default, 0 disables), the runner stops taking new work, writes the
|
|
144
|
+
reason into `queue.json` so `autopilot-status` shows it, and says what to check.
|
|
145
|
+
One successful attempt clears it. A run waiting for a person (`needs-input`) and
|
|
146
|
+
an item arming refused (`blocked-*`) are deliberately not failures.
|
|
147
|
+
|
|
148
|
+
**Telemetry.** `ticks.jsonl` gets one JSON line per tick. The human log answers
|
|
149
|
+
"what happened just now"; a server is only ever asked "how has this been
|
|
150
|
+
behaving for a week".
|
|
151
|
+
|
|
152
|
+
## 5. Self-hosted CI runner
|
|
153
|
+
|
|
154
|
+
ADR-0011 rejected a self-hosted runner because "a self-hosted runner on a
|
|
155
|
+
public repository executes code from any fork's pull request". Half of that has
|
|
156
|
+
changed and half has not, and the difference matters before wiring a runner to
|
|
157
|
+
a machine that holds credentials.
|
|
158
|
+
|
|
159
|
+
Measured, not assumed: the repo is **private**, and it has **one fork**, owned
|
|
160
|
+
by another account. "Zero forks" is a claim worth not making - GitHub requires
|
|
161
|
+
approval before a fork's pull request runs a workflow, but that protection is a
|
|
162
|
+
SETTING, and a self-hosted runner turns "someone approved a workflow run by
|
|
163
|
+
reflex" into code on this machine with this keychain.
|
|
164
|
+
|
|
165
|
+
So: a runner here is defensible on a private single-owner repo, and it is
|
|
166
|
+
defensible only while fork-PR approval stays on and every approval is treated
|
|
167
|
+
as a decision rather than a formality.
|
|
168
|
+
|
|
169
|
+
The same machine can host the runner:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
mkdir ~/actions-runner && cd ~/actions-runner
|
|
173
|
+
# download and configure per GitHub's instructions for the repo
|
|
174
|
+
./svc.sh install && ./svc.sh start
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Two things to keep in mind. The runner service runs in the same session as the
|
|
178
|
+
autopilot agent, so a long CI job and a pipeline run compete for the same CPU
|
|
179
|
+
and the same `~/.claude` state root. And the runner inherits that session's
|
|
180
|
+
keychain, which means a workflow can read the credentials - acceptable on a
|
|
181
|
+
single-owner machine, not on a shared one.
|
|
182
|
+
|
|
183
|
+
## What this page does NOT cover
|
|
184
|
+
|
|
185
|
+
- Turning any of it on. Every step here is the operator's to take.
|
|
186
|
+
- Linux, Windows, systemd, Docker. Out of scope and staying there.
|
|
187
|
+
- Multi-user servers. Everything above assumes one account owns the install,
|
|
188
|
+
the keychain and the queue.
|
package/index.js
CHANGED
|
@@ -69,6 +69,15 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
69
69
|
console.error("uninstall failed:", e.message);
|
|
70
70
|
process.exit(1);
|
|
71
71
|
});
|
|
72
|
+
} else if (command === "verify") {
|
|
73
|
+
// The manifest and this command are the answer to "is my install the thing
|
|
74
|
+
// that was published". Kept out of PLATFORM_EXEMPT on purpose: verifying an
|
|
75
|
+
// install on a host the pipeline refuses to run on answers a question nobody
|
|
76
|
+
// has.
|
|
77
|
+
const { main } = await import(
|
|
78
|
+
pathToFileURL(join(__dirname, "pipeline", "scripts", "verify.mjs")).href
|
|
79
|
+
);
|
|
80
|
+
await main();
|
|
72
81
|
} else if (command === "help") {
|
|
73
82
|
console.log(`
|
|
74
83
|
multi-agent-pipeline - 8-phase AI development pipeline
|
|
@@ -93,6 +102,12 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
93
102
|
npx @mmerterden/multi-agent-pipeline uninstall --all-data ALSO remove pipeline settings + logs/state/metrics
|
|
94
103
|
npx @mmerterden/multi-agent-pipeline uninstall --cursor Legacy pre-v10.7 adapter-file cleanup (also --copilot-chat / --antigravity; --target=<path> overrides cwd)
|
|
95
104
|
|
|
105
|
+
Verify (is this install the thing that was published):
|
|
106
|
+
npx @mmerterden/multi-agent-pipeline verify Package hashes + ~/.claude drift
|
|
107
|
+
npx @mmerterden/multi-agent-pipeline verify --package Package integrity only
|
|
108
|
+
npx @mmerterden/multi-agent-pipeline verify --install Install drift only
|
|
109
|
+
npx @mmerterden/multi-agent-pipeline verify --json
|
|
110
|
+
|
|
96
111
|
Help:
|
|
97
112
|
npx @mmerterden/multi-agent-pipeline help
|
|
98
113
|
npx @mmerterden/multi-agent-pipeline --version Print the installed version
|
|
@@ -111,7 +126,7 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
111
126
|
`);
|
|
112
127
|
} else {
|
|
113
128
|
console.error(`Unknown command: ${command}`);
|
|
114
|
-
console.error("Valid commands: install, uninstall, delete, help, version (--version/-v)");
|
|
129
|
+
console.error("Valid commands: install, uninstall, delete, verify, help, version (--version/-v)");
|
|
115
130
|
console.error("Run with 'help' for usage.");
|
|
116
131
|
process.exit(1);
|
|
117
132
|
}
|
package/install/_common.mjs
CHANGED
|
@@ -141,6 +141,34 @@ export function removePipelineAgentFiles(destDir, agentsSrc) {
|
|
|
141
141
|
* @param {string} dest
|
|
142
142
|
* @param {{ exclude?: Array<string|RegExp>, useSymlinks?: boolean }} [opts]
|
|
143
143
|
*/
|
|
144
|
+
/**
|
|
145
|
+
* Build and editor droppings that live inside a source tree and must never
|
|
146
|
+
* reach a user's install. Python writes `__pycache__/<mod>.cpython-NNN.pyc`
|
|
147
|
+
* next to any script it IMPORTS, so a tree that has merely been used has them;
|
|
148
|
+
* the Finder writes .DS_Store into any directory it has displayed.
|
|
149
|
+
*
|
|
150
|
+
* This is not cosmetic. The installed file count is a gate in this repo, and a
|
|
151
|
+
* CI runner on Python 3.14 shipped 147 files where every developer tree shipped
|
|
152
|
+
* 146 - for four rounds the difference looked like a fixture bug, because the
|
|
153
|
+
* extra file was untracked and therefore invisible to every diff of the SOURCE.
|
|
154
|
+
* Filtering here fixes the user-visible half too: a .pyc compiled against one
|
|
155
|
+
* interpreter is worse than useless on another.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} rel path relative to the copy root, posix-separated
|
|
158
|
+
* @returns {boolean}
|
|
159
|
+
*/
|
|
160
|
+
function isCopyJunk(rel) {
|
|
161
|
+
return (
|
|
162
|
+
rel === ".DS_Store" ||
|
|
163
|
+
rel.endsWith("/.DS_Store") ||
|
|
164
|
+
rel === "__pycache__" ||
|
|
165
|
+
rel.startsWith("__pycache__/") ||
|
|
166
|
+
rel.includes("/__pycache__/") ||
|
|
167
|
+
rel.endsWith("/__pycache__") ||
|
|
168
|
+
rel.endsWith(".pyc")
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
144
172
|
export function copyDir(src, dest, opts = {}) {
|
|
145
173
|
const { exclude = [], useSymlinks = false, skipExisting = false } = opts;
|
|
146
174
|
if (useSymlinks) {
|
|
@@ -156,26 +184,23 @@ export function copyDir(src, dest, opts = {}) {
|
|
|
156
184
|
// user-owned trees (rules/) so an update adds new baseline files but preserves
|
|
157
185
|
// the user's locally-evolved copies. cpSync with force:false + errorOnExist:false
|
|
158
186
|
// silently skips existing files.
|
|
187
|
+
// Normalize to forward slashes so exclude patterns (written posix-style)
|
|
188
|
+
// also match on win32, where cpSync hands back backslash paths.
|
|
189
|
+
const relOf = (source) => source.slice(src.length).split("\\").join("/").replace(/^\//, "");
|
|
190
|
+
// Junk is filtered on EVERY path, including the two that take no exclude
|
|
191
|
+
// list: the point is that the caller never has to remember it.
|
|
192
|
+
const filter = (source) => {
|
|
193
|
+
const rel = relOf(source);
|
|
194
|
+
if (isCopyJunk(rel)) return false;
|
|
195
|
+
return !exclude.some((pat) =>
|
|
196
|
+
typeof pat === "string" ? rel === pat || rel.startsWith(pat + "/") : pat.test(rel),
|
|
197
|
+
);
|
|
198
|
+
};
|
|
159
199
|
if (skipExisting) {
|
|
160
|
-
cpSync(src, dest, { recursive: true, force: false, errorOnExist: false });
|
|
200
|
+
cpSync(src, dest, { recursive: true, force: false, errorOnExist: false, filter });
|
|
161
201
|
return;
|
|
162
202
|
}
|
|
163
|
-
|
|
164
|
-
cpSync(src, dest, { recursive: true, force: true });
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
cpSync(src, dest, {
|
|
168
|
-
recursive: true,
|
|
169
|
-
force: true,
|
|
170
|
-
filter: (source) => {
|
|
171
|
-
// Normalize to forward slashes so exclude patterns (written posix-style)
|
|
172
|
-
// also match on win32, where cpSync hands back backslash paths.
|
|
173
|
-
const rel = source.slice(src.length).split("\\").join("/").replace(/^\//, "");
|
|
174
|
-
return !exclude.some((pat) =>
|
|
175
|
-
typeof pat === "string" ? rel === pat || rel.startsWith(pat + "/") : pat.test(rel),
|
|
176
|
-
);
|
|
177
|
-
},
|
|
178
|
-
});
|
|
203
|
+
cpSync(src, dest, { recursive: true, force: true, filter });
|
|
179
204
|
}
|
|
180
205
|
|
|
181
206
|
/**
|
|
@@ -58,6 +58,10 @@ export const MAINTAINER_SMOKES = new RegExp(
|
|
|
58
58
|
const DEV_ONLY_TOOLING = Object.freeze([
|
|
59
59
|
"run-smokes.mjs",
|
|
60
60
|
"scorecard.mjs",
|
|
61
|
+
// Runs scorecard.mjs from the repo root and diffs its snapshots. In an
|
|
62
|
+
// install that root is $HOME, so the scorecard it shells out to is not
|
|
63
|
+
// there - shipping it would hand the user a command that cannot work.
|
|
64
|
+
"scorecard-snapshot.mjs",
|
|
61
65
|
"lint-skills.mjs",
|
|
62
66
|
"lint-personas.mjs", // imports install/_codex-agents.mjs, which never ships to scripts/
|
|
63
67
|
"lint-mcp-refs.mjs",
|
|
@@ -66,6 +70,10 @@ const DEV_ONLY_TOOLING = Object.freeze([
|
|
|
66
70
|
"validate-schemas.mjs", // validates the repo's own schema files, needs ajv
|
|
67
71
|
"sync-parity-check.sh",
|
|
68
72
|
"benchmark-phase-0.sh",
|
|
73
|
+
// Publish-time only: `prepack` runs it from the repo root to write
|
|
74
|
+
// manifest.json into the tarball. It shells `npm pack` against that root,
|
|
75
|
+
// which in an install is $HOME - so an installed copy could only ever fail.
|
|
76
|
+
"make-manifest.mjs",
|
|
69
77
|
]);
|
|
70
78
|
|
|
71
79
|
/** Eval harnesses (`eval-*.mjs`) read `pipeline/eval/**`, which never ships. */
|