@bongos/core 1.19.306 → 1.19.308
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/.bongos-core.json +39 -19
- package/.claude/skills/builder-ship/SKILL.md +21 -9
- package/.claude/skills/fix-task/SKILL.md +1 -1
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/core-release-pipeline.md +32 -0
- package/docs/recipes/figma-design-loop.md +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/cli-lib.js +53 -0
- package/scripts/gds/go-live.js +666 -0
- package/scripts/gds/ship-honesty.js +150 -0
- package/scripts/gds/ship.js +23 -32
- package/src/module-api.js +5 -1
- package/tests/go_live.mjs +362 -0
- package/tests/route_rank_check.mjs +117 -9
- package/tests/ship_cannot_lie.mjs +168 -0
- package/tests/ship_smoke_gate.mjs +9 -3
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.308",
|
|
6
|
+
"core_contract": "1.19.308",
|
|
7
|
+
"source_commit": "73400ffdb3672b797a1dda57f589d9cc43da2ed1",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-23T20:30:08.216Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 322,
|
|
13
13
|
"agent_docs_stubbed": 22,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1420,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1764,
|
|
20
|
+
"tree_sha256": "bfea309c25b9ba974096b8f0572e268ea80238aaf00e23d700adeb9e58458d49",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
{
|
|
98
98
|
"path": ".claude/skills/builder-ship/SKILL.md",
|
|
99
99
|
"mode": "0000644",
|
|
100
|
-
"sha256": "
|
|
100
|
+
"sha256": "78fcb68ce3ff4b1028904acb9d4d0a37dadd8377108fefef248feef010e4c8b1"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"path": ".claude/skills/builder-stage/SKILL.md",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
{
|
|
143
143
|
"path": ".claude/skills/fix-task/SKILL.md",
|
|
144
144
|
"mode": "0000644",
|
|
145
|
-
"sha256": "
|
|
145
|
+
"sha256": "59b29fcdf54fe51e85191d1a66be237ef35432b76b26a10ec67e46bf08b4d1d3"
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
148
|
"path": ".claude/skills/goal-create/SKILL.md",
|
|
@@ -1662,12 +1662,12 @@
|
|
|
1662
1662
|
{
|
|
1663
1663
|
"path": "docs/recipes/claude-design-loop.md",
|
|
1664
1664
|
"mode": "0000644",
|
|
1665
|
-
"sha256": "
|
|
1665
|
+
"sha256": "389dbbbfb7c941315731d533e300b5d43f40a8aac866c5a1c74c32d5be212a22"
|
|
1666
1666
|
},
|
|
1667
1667
|
{
|
|
1668
1668
|
"path": "docs/recipes/core-release-pipeline.md",
|
|
1669
1669
|
"mode": "0000644",
|
|
1670
|
-
"sha256": "
|
|
1670
|
+
"sha256": "b42d8b4620355bef0e27480b2ddfaa07140e322de988839f0e102f1a1545ac47"
|
|
1671
1671
|
},
|
|
1672
1672
|
{
|
|
1673
1673
|
"path": "docs/recipes/devbox-app-release.md",
|
|
@@ -1687,7 +1687,7 @@
|
|
|
1687
1687
|
{
|
|
1688
1688
|
"path": "docs/recipes/figma-design-loop.md",
|
|
1689
1689
|
"mode": "0000644",
|
|
1690
|
-
"sha256": "
|
|
1690
|
+
"sha256": "6bfee82bddc07a5743316e310dda1e6cb7266fa7805fc1eed67bc8cc17980b46"
|
|
1691
1691
|
},
|
|
1692
1692
|
{
|
|
1693
1693
|
"path": "docs/recipes/gds-db-backup.md",
|
|
@@ -4577,12 +4577,12 @@
|
|
|
4577
4577
|
{
|
|
4578
4578
|
"path": "package-lock.json",
|
|
4579
4579
|
"mode": "0000644",
|
|
4580
|
-
"sha256": "
|
|
4580
|
+
"sha256": "4293f77940579b7c64fa5bc09f5a132c27efb5e4d21ee37a29ee3cf08d60ad88"
|
|
4581
4581
|
},
|
|
4582
4582
|
{
|
|
4583
4583
|
"path": "package.json",
|
|
4584
4584
|
"mode": "0000644",
|
|
4585
|
-
"sha256": "
|
|
4585
|
+
"sha256": "254eba7acbbc7ba707abfe41dd50f8e83880cf8f93cbe5daff2c88cf02dd46f1"
|
|
4586
4586
|
},
|
|
4587
4587
|
{
|
|
4588
4588
|
"path": "public-docs/index.html",
|
|
@@ -5082,7 +5082,7 @@
|
|
|
5082
5082
|
{
|
|
5083
5083
|
"path": "scripts/gds/cli-lib.js",
|
|
5084
5084
|
"mode": "0000644",
|
|
5085
|
-
"sha256": "
|
|
5085
|
+
"sha256": "5aa658b099b2acda06cdb3aecfe905da7de79f2b12c844faca091ff061454017"
|
|
5086
5086
|
},
|
|
5087
5087
|
{
|
|
5088
5088
|
"path": "scripts/gds/code.js",
|
|
@@ -5309,6 +5309,11 @@
|
|
|
5309
5309
|
"mode": "0000644",
|
|
5310
5310
|
"sha256": "75130de4a880fc7d76f5a8d79ed65750238df94847c1ccfdc974188f9300a1e6"
|
|
5311
5311
|
},
|
|
5312
|
+
{
|
|
5313
|
+
"path": "scripts/gds/go-live.js",
|
|
5314
|
+
"mode": "0000644",
|
|
5315
|
+
"sha256": "05734e21763cc0b0614275efa76b7b68afabee8be93f3f2d797400d6a97e86c1"
|
|
5316
|
+
},
|
|
5312
5317
|
{
|
|
5313
5318
|
"path": "scripts/gds/goal.js",
|
|
5314
5319
|
"mode": "0000644",
|
|
@@ -5784,10 +5789,15 @@
|
|
|
5784
5789
|
"mode": "0000644",
|
|
5785
5790
|
"sha256": "087def1396283b19cb8a6deea4cc7f362d7a2f164d496a587220f0a48568aae6"
|
|
5786
5791
|
},
|
|
5792
|
+
{
|
|
5793
|
+
"path": "scripts/gds/ship-honesty.js",
|
|
5794
|
+
"mode": "0000644",
|
|
5795
|
+
"sha256": "32827178a46cc01c36c422e1adef433be99492e8444a8affbe51e5e8c1e8ce9f"
|
|
5796
|
+
},
|
|
5787
5797
|
{
|
|
5788
5798
|
"path": "scripts/gds/ship.js",
|
|
5789
5799
|
"mode": "0000644",
|
|
5790
|
-
"sha256": "
|
|
5800
|
+
"sha256": "f3005175abc0ba20e958381df6aee02402267e9956da466386656dd4e86ad8cf"
|
|
5791
5801
|
},
|
|
5792
5802
|
{
|
|
5793
5803
|
"path": "scripts/gds/smoke-batch-claim.sh",
|
|
@@ -6242,7 +6252,7 @@
|
|
|
6242
6252
|
{
|
|
6243
6253
|
"path": "src/module-api.js",
|
|
6244
6254
|
"mode": "0000644",
|
|
6245
|
-
"sha256": "
|
|
6255
|
+
"sha256": "87cec9eba20525297a298914c20d89882db138f0cdbc5c1ae4a5ce3d285147db"
|
|
6246
6256
|
},
|
|
6247
6257
|
{
|
|
6248
6258
|
"path": "src/module-loader/catalog.js",
|
|
@@ -7159,6 +7169,11 @@
|
|
|
7159
7169
|
"mode": "0000644",
|
|
7160
7170
|
"sha256": "d0d10628a17717fdef505dcf501915ecfcc31a0c99d9e5d4cf6dc06b47d8c883"
|
|
7161
7171
|
},
|
|
7172
|
+
{
|
|
7173
|
+
"path": "tests/go_live.mjs",
|
|
7174
|
+
"mode": "0000644",
|
|
7175
|
+
"sha256": "90c8cbfd8bf0b446261f9fd837b9b08b1035210f1cbf1f7ff81998ec535c32f1"
|
|
7176
|
+
},
|
|
7162
7177
|
{
|
|
7163
7178
|
"path": "tests/goal_abuse_matrix.mjs",
|
|
7164
7179
|
"mode": "0000644",
|
|
@@ -8282,7 +8297,7 @@
|
|
|
8282
8297
|
{
|
|
8283
8298
|
"path": "tests/route_rank_check.mjs",
|
|
8284
8299
|
"mode": "0000644",
|
|
8285
|
-
"sha256": "
|
|
8300
|
+
"sha256": "bdc58224050392ece492ae8e8f12f62a8934d86e3993e0df5b8cb0aac80602e8"
|
|
8286
8301
|
},
|
|
8287
8302
|
{
|
|
8288
8303
|
"path": "tests/route_validation.mjs",
|
|
@@ -8504,6 +8519,11 @@
|
|
|
8504
8519
|
"mode": "0000644",
|
|
8505
8520
|
"sha256": "afdb09a3ee072fea82390a187c12c42d8249fce0ce84644a4b8dff6a643dd009"
|
|
8506
8521
|
},
|
|
8522
|
+
{
|
|
8523
|
+
"path": "tests/ship_cannot_lie.mjs",
|
|
8524
|
+
"mode": "0000644",
|
|
8525
|
+
"sha256": "82f6f951cc50c34860545be0f309e18a796ebb5baa712ccb341e1fd0dd72a233"
|
|
8526
|
+
},
|
|
8507
8527
|
{
|
|
8508
8528
|
"path": "tests/ship_card.mjs",
|
|
8509
8529
|
"mode": "0000644",
|
|
@@ -8552,7 +8572,7 @@
|
|
|
8552
8572
|
{
|
|
8553
8573
|
"path": "tests/ship_smoke_gate.mjs",
|
|
8554
8574
|
"mode": "0000644",
|
|
8555
|
-
"sha256": "
|
|
8575
|
+
"sha256": "6ce7bd5d2a8910e201a9869c8a3f22ef210fd4400fbd14777d8d15d13252a97e"
|
|
8556
8576
|
},
|
|
8557
8577
|
{
|
|
8558
8578
|
"path": "tests/ship_upload_skip.mjs",
|
|
@@ -7,7 +7,7 @@ You are shipping a task on behalf of the current builder. Shipping is the moment
|
|
|
7
7
|
|
|
8
8
|
## What this skill does
|
|
9
9
|
|
|
10
|
-
Runs `node scripts/gds/ship.js <task-id> --notes
|
|
10
|
+
Runs `node scripts/gds/ship.js <task-id> --notes-file notes.md --summary-file summary.txt`.
|
|
11
11
|
|
|
12
12
|
The CLI drives a **three-state lifecycle** with a subagent grader between states (Phase 5, 2026-05-10):
|
|
13
13
|
|
|
@@ -34,7 +34,7 @@ The CLI drives a **three-state lifecycle** with a subagent grader between states
|
|
|
34
34
|
|
|
35
35
|
**When you see that block, position the sandbox link prominently for the builder.** `ship.js` also emits the deterministic lifecycle card for the `review` stage (an `[otb-card-html] <path>` marker) — if an in-feed visualization tool is available, render that card (its primary action is **Open in sandbox ↗**, the URL); it's the cleanest way to make the link impossible to miss. Either way, surface the URL as its own standalone, clickable line in chat — a clear callout, NOT buried inside a paragraph of command output — and say in one line what to look at. This is the moment the change is ready to review; the link must be impossible to miss. Let the builder open it, review, and suggest changes; iterate if needed. **Only once the builder explicitly approves, re-run with `node scripts/gds/ship.js <id> --approved`** — that records their sign-off as a durable `sandbox_review` fact on the task (who, when, and the content stamp of exactly what was reviewed). Never pass `--approved` yourself without a real human go; the pause + sign-off IS the feature. `--skip-stage` is the escape hatch (use sparingly — the gate exists so unreviewed game changes don't ride straight to prod). Laptop sessions and non-game diffs are unaffected.
|
|
36
36
|
|
|
37
|
-
2. **Compose the handoff notes (`--notes`)** following the structure in `docs/handoff-template.md`:
|
|
37
|
+
2. **Compose the handoff notes (`--notes-file`)** following the structure in `docs/handoff-template.md`:
|
|
38
38
|
- What shipped (with confidence tags: verified-prod | verified-smoke | implemented-not-verified)
|
|
39
39
|
- What's broken or unverified
|
|
40
40
|
- Manual checks the next session should run first
|
|
@@ -52,16 +52,28 @@ The CLI drives a **three-state lifecycle** with a subagent grader between states
|
|
|
52
52
|
- "Sessions can claim work atomically; merge conflicts can't happen across worktrees."
|
|
53
53
|
No jargon, no file paths, no commit hashes.
|
|
54
54
|
|
|
55
|
-
4. **Run
|
|
55
|
+
4. **Run** — write the prose to files and pass the file (task 1003168):
|
|
56
56
|
```bash
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
...handoff markdown...
|
|
60
|
-
EOF
|
|
61
|
-
)" \
|
|
62
|
-
--summary "the one-liner"
|
|
57
|
+
# write notes.md and summary.txt with your editor/Write tool first
|
|
58
|
+
node scripts/gds/ship.js <task-id> --notes-file notes.md --summary-file summary.txt
|
|
63
59
|
```
|
|
64
60
|
|
|
61
|
+
**Use the `-file` forms.** Handoff notes are technical prose, so they contain
|
|
62
|
+
backticks — and a backtick in an inline `--notes "..."` is command-substituted
|
|
63
|
+
by the shell *before* node runs: the span executes, its output replaces it, and
|
|
64
|
+
the rest of the argument list is truncated. That is not hypothetical; it shipped
|
|
65
|
+
a task with **empty notes and no summary**, so the ledger held nothing about a
|
|
66
|
+
session that had happened (learning 1000197). A file argument is the one form
|
|
67
|
+
the shell cannot rewrite. `-` reads stdin if you'd rather pipe.
|
|
68
|
+
|
|
69
|
+
Inline `--notes "..."` / `--summary "..."` still work for short prose with no
|
|
70
|
+
backticks, `$`, or newlines. The older documented form —
|
|
71
|
+
`--notes "$(cat <<'EOF' … EOF)"` — is *also* shell-safe, because the quoted
|
|
72
|
+
heredoc delimiter suppresses expansion; it is simply easy to get subtly wrong,
|
|
73
|
+
and when you do the failure is silent. Passing both `--notes` and
|
|
74
|
+
`--notes-file` is refused rather than resolved by precedence, so nothing you
|
|
75
|
+
wrote is dropped without a word.
|
|
76
|
+
|
|
65
77
|
**Never merge a task branch to main before running `ship.js`.** `ship.js` diffs the claim branch against `main`; an already-merged branch yields an empty diff, and the only exits are `--allow-empty` (dead-ends the task at `completed`, requiring an Archon-only confirm) or reverting the merge. Ship from the branch, then let `ship.js` land it. (This trap was hit on tasks 1002417 and 1002419, producing blockers 1000081/1000082 and an ~8.5-hour stall.)
|
|
66
78
|
|
|
67
79
|
5. **Render the completion card — it IS the summary; do NOT write a prose recap (task 1270).**
|
|
@@ -39,7 +39,7 @@ Runs the recurring "claim, fix, and ship task NNNN" macro end to end. Its own va
|
|
|
39
39
|
|
|
40
40
|
6. **Ship from the claim branch** — see `/builder-ship` for handoff notes, the value summary, and the completion-card rendering contract. In short:
|
|
41
41
|
```
|
|
42
|
-
node scripts/gds/ship.js <id> --notes
|
|
42
|
+
node scripts/gds/ship.js <id> --notes-file notes.md --summary-file summary.txt
|
|
43
43
|
```
|
|
44
44
|
**Never merge the claim branch into `main` before running `ship.js`.** `ship.js` diffs the branch against `main`; a pre-merged branch yields an empty diff, and the only way out is `--allow-empty` (dead-ends the task at `completed`, needing an Archon-only confirm) or reverting the merge. Ship from the branch and let `ship.js` land it — the same trap `/builder-ship` calls out.
|
|
45
45
|
|
|
@@ -113,7 +113,7 @@ The adapter writes files to `src/ui/<surfaceId>/` and updates `config/design-sou
|
|
|
113
113
|
```bash
|
|
114
114
|
git add src/ui/hall-ui/ config/design-sources.json
|
|
115
115
|
git commit -m "ui(hall-ui): Claude Design export (task 1234)"
|
|
116
|
-
node scripts/gds/ship.js 1234 --notes
|
|
116
|
+
node scripts/gds/ship.js 1234 --notes-file notes.md --summary-file summary.txt
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
---
|
|
@@ -136,6 +136,38 @@ The generic mechanics are [upgrading-the-core](upgrading-the-core.md); this is t
|
|
|
136
136
|
|
|
137
137
|
**Cadence: bump once, at session end.** One upgrade covers every release the session landed (publish-on-merge cuts one per merge — you don't chase each). Don't bump per-ship.
|
|
138
138
|
|
|
139
|
+
### Step 0 — try the one command first (task 1003189)
|
|
140
|
+
|
|
141
|
+
The whole sequence below is deterministic, so it is also a script. `go-live.js` composes the
|
|
142
|
+
same parts this runbook narrates — `deploy-divergence.js` for the gap, the registry for proof a
|
|
143
|
+
release exists, and `bongos upgrade --registry --commit-pin` for the bump itself — and reports
|
|
144
|
+
once instead of forty times:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
node scripts/gds/go-live.js
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
That is **plan-only and read-only**: it prints live-vs-target, checks the registry actually
|
|
151
|
+
carries the target, and names every halt condition it found. Add `--apply` to move the pin.
|
|
152
|
+
|
|
153
|
+
It **halts rather than guessing** on exactly the things that need judgement — unreleased commits
|
|
154
|
+
on `main`, a registry missing the target, a dirty tree on either end, a failed health check, an
|
|
155
|
+
uncommitted pin. When it halts, fall through to the manual steps below and use your own head;
|
|
156
|
+
that is what the rest of this section is for.
|
|
157
|
+
|
|
158
|
+
Two flags worth knowing:
|
|
159
|
+
|
|
160
|
+
- `--verify-marker <path>=<needle>` refuses a target that does not actually contain your change.
|
|
161
|
+
This is the guard for the trap in Step 1: the carrier sitting on `main` when your fix merged
|
|
162
|
+
was usually cut *before* it, so deploying "the version main was on" is a textbook-clean go-live
|
|
163
|
+
that changes nothing — and every downstream check agrees it worked.
|
|
164
|
+
- `--allow-unreleased` downgrades the UNRELEASED halt to a warning. Reach for it only when you
|
|
165
|
+
have already confirmed *why* main is ahead of its last release.
|
|
166
|
+
|
|
167
|
+
Host topology (ssh alias, box paths, service names, health URL) lives in a host-owned
|
|
168
|
+
`config/go-live.json`, never in the core — run `node scripts/gds/go-live.js --help` for the
|
|
169
|
+
schema. A core that named one instance's box would not be a core.
|
|
170
|
+
|
|
139
171
|
### Step 1 — pick the version (your machine, main checkout)
|
|
140
172
|
|
|
141
173
|
```bash
|
|
@@ -98,7 +98,7 @@ The adapter translates frames/text/solid-fill rectangles into `.js` + `.css` fil
|
|
|
98
98
|
```bash
|
|
99
99
|
git add src/ui/hall-ui/ config/design-sources.json
|
|
100
100
|
git commit -m "ui(hall-ui): Figma export (task 1234)"
|
|
101
|
-
node scripts/gds/ship.js 1234 --notes
|
|
101
|
+
node scripts/gds/ship.js 1234 --notes-file notes.md --summary-file summary.txt
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
---
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.308",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.308",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.308",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/scripts/gds/cli-lib.js
CHANGED
|
@@ -266,6 +266,58 @@ function stripBom(s) {
|
|
|
266
266
|
return s.charCodeAt(0) === 0xfeff ? s.slice(1) : s;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
// argText(name, args, io) — resolve prose passed EITHER inline (`--notes "..."`)
|
|
270
|
+
// or from a file (`--notes-file path`, or `-` for stdin). Returns
|
|
271
|
+
// { value, source, error }: `value` is a string ('' when neither flag is
|
|
272
|
+
// present), `source` is 'inline' | 'file' | 'stdin' | 'none', and `error` is a
|
|
273
|
+
// caller-printable string when the request was refused or unreadable.
|
|
274
|
+
//
|
|
275
|
+
// WHY THIS EXISTS (learning 1000197). Prose handed to a CLI inline goes through
|
|
276
|
+
// the SHELL first. A handoff note containing backticks — which technical prose
|
|
277
|
+
// constantly does — is command-substituted by bash before node ever runs: the
|
|
278
|
+
// backticked span executes, its output replaces it, and the rest of the argument
|
|
279
|
+
// list is truncated. That is not a theoretical hazard; it silently shipped a task
|
|
280
|
+
// with EMPTY notes and no summary, so the ledger recorded nothing about a session
|
|
281
|
+
// that had happened. A file argument is the only form the shell cannot rewrite.
|
|
282
|
+
//
|
|
283
|
+
// BOTH-GIVEN IS AN ERROR, never a silent precedence. Picking one would mean the
|
|
284
|
+
// caller's other input vanished without a word — the same class of quiet loss
|
|
285
|
+
// this helper exists to end (the capture.js --spark/--full refusal precedent).
|
|
286
|
+
function argText(name, args = process.argv.slice(2), io = {}) {
|
|
287
|
+
const readFile = io.readFile || ((f) => require('fs').readFileSync(f, 'utf8'));
|
|
288
|
+
const readStdin = io.readStdin || (() => require('fs').readFileSync(0, 'utf8'));
|
|
289
|
+
const fileFlag = `${name}-file`;
|
|
290
|
+
const inline = arg(name, args);
|
|
291
|
+
// arg() returns undefined (not null) for a flag present as the LAST token, so
|
|
292
|
+
// presence must be tested separately — otherwise `--notes-file` with no path
|
|
293
|
+
// slips past the null check and resolves to an empty string, which is the very
|
|
294
|
+
// silent-empty outcome this helper exists to prevent.
|
|
295
|
+
const filePresent = args.includes(fileFlag) || args.some((a) => a.startsWith(`${fileFlag}=`));
|
|
296
|
+
const file = filePresent ? arg(fileFlag, args) : null;
|
|
297
|
+
|
|
298
|
+
if (inline != null && filePresent) {
|
|
299
|
+
return {
|
|
300
|
+
value: '', source: 'none',
|
|
301
|
+
error: `${name} and ${fileFlag} were both given. Pass one — ${fileFlag} is the safe form for prose `
|
|
302
|
+
+ '(a shell rewrites backticks in an inline value before node sees it). Refusing rather than '
|
|
303
|
+
+ 'silently picking one, so nothing you wrote is dropped without a word.',
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (filePresent) {
|
|
307
|
+
if (file == null || file === '' || file.startsWith('--')) {
|
|
308
|
+
return { value: '', source: 'none', error: `${fileFlag} needs a path (or - for stdin).` };
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const raw = file === '-' ? readStdin() : readFile(file);
|
|
312
|
+
return { value: stripBom(String(raw)), source: file === '-' ? 'stdin' : 'file', error: null };
|
|
313
|
+
} catch (err) {
|
|
314
|
+
return { value: '', source: 'none', error: `could not read ${fileFlag} ${file}: ${err.message}` };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (inline != null) return { value: inline, source: 'inline', error: null };
|
|
318
|
+
return { value: '', source: 'none', error: null };
|
|
319
|
+
}
|
|
320
|
+
|
|
269
321
|
// resolveQuiet(args, env) — true when api.js's stderr status banner should be
|
|
270
322
|
// suppressed: the --quiet flag or the GDS_QUIET env var. Both default to the
|
|
271
323
|
// live process, matching hasFlag()'s own default-args contract. Default OFF —
|
|
@@ -853,6 +905,7 @@ module.exports = {
|
|
|
853
905
|
loadSessionSync,
|
|
854
906
|
saveSession,
|
|
855
907
|
arg,
|
|
908
|
+
argText,
|
|
856
909
|
hasFlag,
|
|
857
910
|
stripBom,
|
|
858
911
|
repairMsysMangledPath,
|