@groundnuty/macf 0.2.17 → 0.2.18

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-files.d.ts","sourceRoot":"","sources":["../../src/cli/env-files.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAiHnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAwB3C;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CA6BnE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAkEjE;AAMD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CA+BhE;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAuCnE;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,eAAe,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CA8DR;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAiC/D;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,GACtB,mBAAmB,CA2BrB"}
@@ -0,0 +1,585 @@
1
+ /**
2
+ * Per-concern env-file generators for the multi-file workspace env layout
3
+ * (groundnuty/macf#342). PR-A of a 4-PR sequence:
4
+ *
5
+ * PR-A (this PR) — pure generators only; no init/update wiring
6
+ * PR-B — refactor claude.sh to source the per-concern files
7
+ * + wire writers into init/update
8
+ * PR-C — migration: detect legacy monolithic claude.sh +
9
+ * compat with settings.local.json env block
10
+ * PR-D — operator docs
11
+ *
12
+ * Each generator takes a `MacfAgentConfig` and returns the textual content
13
+ * for one per-concern env file written under `<workspace>/.claude/.macf/`.
14
+ *
15
+ * Concern boundaries (operator-managed vs macf-managed) come from #342:
16
+ * env.identity — macf-managed — identity vars (set BEFORE tmux self-wrap)
17
+ * env.github — macf-managed — App creds + GH_TOKEN mint + git ident
18
+ * (EMPTY in local-mode per DR-024)
19
+ * env.certs — macf-managed — cert + log paths
20
+ * env.registry — macf-managed — MACF_REGISTRY_TYPE + per-type vars
21
+ * env.telemetry — operator-managed — OTel gates + endpoint
22
+ * (MINIMAL when MACF_OTEL_DISABLED=1)
23
+ * env.tmux — operator-managed — MACF_TMUX_SESSION + MACF_TMUX_WINDOW
24
+ * (MINIMAL when neither set in config)
25
+ *
26
+ * **Pure functions** — no file I/O, no side effects. Disk writes land in
27
+ * PR-B's `writeEnvFiles()` helper.
28
+ *
29
+ * **Faithful reproduction** — each generator emits the SAME export-line
30
+ * shapes as the existing monolithic `claude-sh.ts`. The per-concern logic
31
+ * is duplicated here rather than imported from `claude-sh.ts` because the
32
+ * existing helpers (`registryEnvLines`, `caPathLines`,
33
+ * `githubAppEnvLines`, `githubTokenAndIdentityLines`) are file-private.
34
+ * PR-B will extract a shared util module and dedup.
35
+ *
36
+ * **Schema versioning** — each emitted file carries a `# schema_version: 1`
37
+ * comment near the top so PR-C's migration tool (and any future bumps) can
38
+ * detect the format version without parsing.
39
+ */
40
+ import { mkdirSync, writeFileSync } from 'node:fs';
41
+ import { join, resolve } from 'node:path';
42
+ // ---------------------------------------------------------------------------
43
+ // Headers
44
+ // ---------------------------------------------------------------------------
45
+ /**
46
+ * Header text for macf-managed files. Mirrors the warning shape in
47
+ * `claude-sh.ts`'s `MANAGED_HEADER_LINES` so operators see consistent
48
+ * "do not edit" framing across all generated files. Edit the canonical
49
+ * generator and re-run `macf update`.
50
+ */
51
+ function managedHeaderLines(generator) {
52
+ return [
53
+ '# This file is managed by `macf`. Do not edit directly — edits are',
54
+ '# overwritten on the next `macf update`. The template lives at',
55
+ `# groundnuty/macf:src/cli/env-files.ts (\`${generator}\`). To change`,
56
+ '# the generated content, file an issue or PR against that file, then',
57
+ '# run `macf update` here.',
58
+ '#',
59
+ '# This file is sourced (not executed) by claude.sh; vars use `export`',
60
+ '# so they propagate to claude\'s child processes.',
61
+ ];
62
+ }
63
+ /**
64
+ * Header text for operator-managed files. Softer wording than the macf-
65
+ * managed variant — operators are free to edit, and `macf update`
66
+ * preserves their changes (PR-B + PR-C wire that contract; this file just
67
+ * declares the intent in the header).
68
+ */
69
+ function operatorHeaderLines(generator) {
70
+ return [
71
+ `# This file is operator-editable. \`macf update\` will preserve your`,
72
+ `# edits as long as the file exists; the canonical default content`,
73
+ `# is generated by ${generator} in groundnuty/macf:src/cli/env-files.ts`,
74
+ '# and re-emitted only when the file is missing entirely.',
75
+ '#',
76
+ '# This file is sourced (not executed) by claude.sh; vars use `export`',
77
+ '# so they propagate to claude\'s child processes.',
78
+ ];
79
+ }
80
+ /**
81
+ * Header text for the `env._helpers` library file. Macf-managed (operators
82
+ * shouldn't override the helper definitions ad-hoc), but framed as a
83
+ * library/sourced-utility rather than a config file — no var exports, just
84
+ * function definitions consumed by sibling env.* files.
85
+ */
86
+ function libraryHeaderLines(generator) {
87
+ return [
88
+ '# This file is managed by `macf`. Do not edit directly — edits are',
89
+ '# overwritten on the next `macf update`. The template lives at',
90
+ `# groundnuty/macf:src/cli/env-files.ts (\`${generator}\`). To change`,
91
+ '# the generated content, file an issue or PR against that file, then',
92
+ '# run `macf update` here.',
93
+ '#',
94
+ '# This is a LIBRARY file: it defines shell FUNCTIONS used by the other',
95
+ '# env.* files when claude.sh sources them. The underscore prefix sorts',
96
+ '# this file BEFORE alphabetical-letter siblings under `env.*`, so the',
97
+ '# functions are defined before any caller is sourced (function-not-',
98
+ '# defined hazard guard).',
99
+ ];
100
+ }
101
+ const SCHEMA_VERSION_LINE = '# schema_version: 1';
102
+ /**
103
+ * Assemble final file content from header + body lines. Joins with `\n`
104
+ * and ensures trailing newline. The schema_version line goes right after
105
+ * the header so it's the first non-comment-block content.
106
+ */
107
+ function assemble(header, body) {
108
+ return [
109
+ ...header,
110
+ SCHEMA_VERSION_LINE,
111
+ '',
112
+ ...body,
113
+ '',
114
+ ].join('\n');
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Local-mode + path helpers (mirrored from claude-sh.ts; PR-B dedup target)
118
+ // ---------------------------------------------------------------------------
119
+ /**
120
+ * True when this config runs in local-registry mode (DR-024 / macf#322).
121
+ * Mirrored from `claude-sh.ts`'s file-private `isLocalMode`.
122
+ */
123
+ function isLocalMode(cfg) {
124
+ return cfg.registry.type === 'local';
125
+ }
126
+ /**
127
+ * Compute POSIX-style dirname without pulling in node:path. Mirrored from
128
+ * `claude-sh.ts`'s file-private `posixDirname`. The launcher always runs
129
+ * on POSIX-shaped filesystems (DR-024 §threat model).
130
+ */
131
+ function posixDirname(p) {
132
+ const idx = p.lastIndexOf('/');
133
+ if (idx < 0)
134
+ return '.';
135
+ if (idx === 0)
136
+ return '/';
137
+ return p.slice(0, idx);
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // env._helpers — macf-managed; library file, sourced first per alphabetical order
141
+ // ---------------------------------------------------------------------------
142
+ /**
143
+ * Generate `.claude/.macf/env._helpers` content.
144
+ *
145
+ * Defines shell helper FUNCTIONS used by sibling env.* files. The
146
+ * underscore prefix sorts this file BEFORE alphabetical-letter siblings
147
+ * (`env.certs`, `env.github`, `env.identity`, etc.), so when claude.sh
148
+ * sources `env.*` in alphabetical order via shell glob, this file's
149
+ * functions are defined before any caller is sourced.
150
+ *
151
+ * Currently exports one helper:
152
+ *
153
+ * macf_settings_get(var_name)
154
+ * Reads `.env.<var_name>` from `<workspace>/.claude/settings.local.json`
155
+ * via jq. Returns empty string if the file/key is missing or jq isn't
156
+ * installed. Used by:
157
+ * - env.identity — 3-layer settings priority for MACF_AGENT_NAME / ROLE
158
+ * - env.telemetry — 4-layer endpoint chain for MACF_OTEL_ENDPOINT
159
+ *
160
+ * **No exports** — this is a function-definition file. Sourcing it has
161
+ * no observable effect beyond making functions callable in the sourcing
162
+ * shell. macf#313 introduced the helper inline in claude.sh; macf#342
163
+ * extracted it here so multiple env.* files can share it without each
164
+ * file having to redeclare it (PR-A had `macf_settings_get` duplicated
165
+ * inside generateEnvIdentity to make env.identity self-contained;
166
+ * macf#342 PR-B moves the single source-of-truth into env._helpers).
167
+ *
168
+ * Pure function — no I/O. Doesn't take a config arg because the helper
169
+ * body is config-independent (it reads from settings.local.json at
170
+ * runtime, not from baked values).
171
+ */
172
+ export function generateEnvHelpers() {
173
+ const body = [
174
+ '# Generator: generateEnvHelpers (env-files.ts)',
175
+ '#',
176
+ '# Sourced by claude.sh BEFORE any sibling env.* file (underscore prefix',
177
+ '# sorts before alphabetical letters in shell glob expansion). Defines',
178
+ '# helper functions used by env.identity (3-layer priority for AGENT_NAME',
179
+ '# / ROLE) and env.telemetry (4-layer endpoint chain). Single source of',
180
+ '# truth — pre-#342 the helper was inlined in claude.sh; pre-#342 PR-B',
181
+ '# it was duplicated inside generateEnvIdentity. This file dedupes both.',
182
+ '',
183
+ '# Settings-driven identity helper (macf#313). Reads `.env.<NAME>` from',
184
+ '# .claude/settings.local.json via jq; returns empty string if file/key',
185
+ '# missing or jq absent. Identity-override blocks use it to prefer',
186
+ '# operator-edited settings.local.json over baked defaults, without',
187
+ '# forcing operators to edit any generated env.* file.',
188
+ 'macf_settings_get() {',
189
+ ' local var_name="$1"',
190
+ ' if [ -f "$SCRIPT_DIR/.claude/settings.local.json" ] && command -v jq >/dev/null 2>&1; then',
191
+ ' jq -r ".env.${var_name} // empty" "$SCRIPT_DIR/.claude/settings.local.json" 2>/dev/null',
192
+ ' fi',
193
+ '}',
194
+ ];
195
+ return assemble(libraryHeaderLines('generateEnvHelpers'), body);
196
+ }
197
+ // ---------------------------------------------------------------------------
198
+ // env.identity — macf-managed
199
+ // ---------------------------------------------------------------------------
200
+ /**
201
+ * Generate `.claude/.macf/env.identity` content.
202
+ *
203
+ * The 5 identity vars currently set BEFORE the tmux self-wrap in
204
+ * `claude-sh.ts`:
205
+ *
206
+ * MACF_WORKSPACE_DIR — absolute path to the workspace (cross-repo cwd
207
+ * safety, see #161 + coordination.md)
208
+ * MACF_PROJECT — project name
209
+ * MACF_AGENT_NAME — agent identifier (with 3-layer settings priority)
210
+ * MACF_AGENT_ROLE — agent role (with 3-layer settings priority)
211
+ * MACF_AGENT_TYPE — `permanent` | `worker`
212
+ *
213
+ * MACF_AGENT_NAME and MACF_AGENT_ROLE preserve the post-#313 settings-driven
214
+ * 3-layer priority (env > settings.local.json > baked default). Reading
215
+ * settings.local.json requires the `macf_settings_get` helper, which is
216
+ * defined in `env._helpers` (sourced before this file alphabetically by
217
+ * claude.sh's `for f in env.*` glob — underscore prefix sorts first).
218
+ *
219
+ * **Note on SCRIPT_DIR**: `MACF_WORKSPACE_DIR=$SCRIPT_DIR` requires that
220
+ * the sourcing script (claude.sh) sets SCRIPT_DIR before sourcing. That
221
+ * contract is documented in claude.sh; this file trusts it.
222
+ */
223
+ export function generateEnvIdentity(config) {
224
+ const body = [
225
+ `# Generator: generateEnvIdentity (env-files.ts)`,
226
+ `# Sourced by claude.sh BEFORE the tmux self-wrap so identity vars are`,
227
+ `# present in the env captured by the \`-e VAR=VAL\` pass-through (macf#340).`,
228
+ '#',
229
+ '# `macf_settings_get` is defined in env._helpers (sourced first per',
230
+ '# alphabetical order — underscore prefix sorts before letters). Calls',
231
+ '# below resolve at runtime against the function defined there.',
232
+ '',
233
+ '# Cross-repo path resolution (#140 + #161). Runtime templates reference',
234
+ '# $MACF_WORKSPACE_DIR for absolute-path commands so cd-ing to another',
235
+ '# repo doesn\'t break helper invocations.',
236
+ 'export MACF_WORKSPACE_DIR="$SCRIPT_DIR"',
237
+ `export MACF_PROJECT="${config.project}"`,
238
+ `export MACF_AGENT_TYPE="${config.agent_type}"`,
239
+ '',
240
+ '# Settings-driven identity overrides (macf#313). Three-layer priority:',
241
+ '# 1. Already-set env var (operator: `MACF_AGENT_NAME=foo ./claude.sh`)',
242
+ '# 2. .claude/settings.local.json `env` block',
243
+ '# 3. Baked default from macf init/update (this generated file)',
244
+ `MACF_AGENT_NAME="\${MACF_AGENT_NAME:-$(macf_settings_get MACF_AGENT_NAME)}"`,
245
+ `MACF_AGENT_NAME="\${MACF_AGENT_NAME:-${config.agent_name}}"`,
246
+ 'export MACF_AGENT_NAME',
247
+ `MACF_AGENT_ROLE="\${MACF_AGENT_ROLE:-$(macf_settings_get MACF_AGENT_ROLE)}"`,
248
+ `MACF_AGENT_ROLE="\${MACF_AGENT_ROLE:-${config.agent_role}}"`,
249
+ 'export MACF_AGENT_ROLE',
250
+ ];
251
+ return assemble(managedHeaderLines('generateEnvIdentity'), body);
252
+ }
253
+ // ---------------------------------------------------------------------------
254
+ // env.github — macf-managed; EMPTY in local-mode
255
+ // ---------------------------------------------------------------------------
256
+ /**
257
+ * Generate `.claude/.macf/env.github` content.
258
+ *
259
+ * Emits APP_ID / INSTALL_ID / KEY_PATH App-cred exports + the relative-
260
+ * path resolver + the fail-loud `macf-gh-token.sh` invocation + git
261
+ * author/committer name exports.
262
+ *
263
+ * **Empty placeholder in local-mode** (cfg.registry.type === 'local',
264
+ * DR-024). The body is just the header + schema_version + a comment
265
+ * explaining why — no GitHub App tokens are minted in local-registry
266
+ * mode (commits land as the local user, not as `app/<bot>[bot]`). The
267
+ * absence of GH_TOKEN-mint logic is what makes local-mode work without
268
+ * GitHub credentials.
269
+ */
270
+ export function generateEnvGitHub(config) {
271
+ if (isLocalMode(config)) {
272
+ const body = [
273
+ '# Generator: generateEnvGitHub (env-files.ts)',
274
+ '#',
275
+ '# Intentionally empty for local-mode workspace. Per DR-024 / macf#322,',
276
+ '# local-registry mode does not mint a GitHub App installation token —',
277
+ '# coordination uses the local registry file at $MACF_REGISTRY_PATH, and',
278
+ '# commits land as the local user (not as `app/<bot>[bot]`). No APP_ID,',
279
+ '# INSTALL_ID, KEY_PATH, GH_TOKEN, or GIT_AUTHOR_NAME/GIT_COMMITTER_NAME',
280
+ '# are exported. See DR-024 §"Routing trade-offs".',
281
+ `echo "Starting ${config.agent_name} (${config.agent_role}) [local-registry mode]..."`,
282
+ ];
283
+ return assemble(managedHeaderLines('generateEnvGitHub'), body);
284
+ }
285
+ const githubApp = config.github_app;
286
+ // Defensive — schema marks github_app optional, but non-local mode
287
+ // requires it (init.ts enforces the pairing at write time per
288
+ // config.ts comments). If it's missing here, the workspace config is
289
+ // broken; surface that visibly rather than emitting partial exports.
290
+ if (!githubApp) {
291
+ const body = [
292
+ '# Generator: generateEnvGitHub (env-files.ts)',
293
+ '#',
294
+ '# WARNING: this workspace config has registry.type !== "local" but no',
295
+ '# `github_app` block. Re-run `macf init --force` to repair.',
296
+ ];
297
+ return assemble(managedHeaderLines('generateEnvGitHub'), body);
298
+ }
299
+ const body = [
300
+ '# Generator: generateEnvGitHub (env-files.ts)',
301
+ '',
302
+ '# GitHub App credentials. KEY_PATH is resolved against $SCRIPT_DIR if',
303
+ '# relative (cross-repo cwd safety per #140 + #161 — without this, cd-ing',
304
+ '# to another repo breaks the token helper and triggers the attribution',
305
+ '# trap).',
306
+ `export APP_ID="${githubApp.app_id}"`,
307
+ `export INSTALL_ID="${githubApp.install_id}"`,
308
+ `export KEY_PATH="${githubApp.key_path}"`,
309
+ 'case "$KEY_PATH" in',
310
+ ' /*) ;; # already absolute',
311
+ ' *) KEY_PATH="$SCRIPT_DIR/$KEY_PATH" ;;',
312
+ 'esac',
313
+ 'export KEY_PATH',
314
+ '',
315
+ '# Bot token generation — fail loud. The helper validates the ghs_ prefix',
316
+ '# and surfaces diagnostics (clock drift, bad key, wrong App/install ID).',
317
+ '# Do NOT inline the bare CLI here — without pipefail, a failed fetch piped',
318
+ '# through jq would succeed, GH_TOKEN would become "null", and Claude Code',
319
+ '# would silently fall back to stored `gh auth login` as the user. See the',
320
+ '# attribution-trap section of coordination.md Token & Git Hygiene.',
321
+ 'GH_TOKEN=$("$SCRIPT_DIR/.claude/scripts/macf-gh-token.sh" \\',
322
+ ' --app-id "$APP_ID" --install-id "$INSTALL_ID" --key "$KEY_PATH") || {',
323
+ ' echo "FATAL: bot token generation failed — see stderr above." >&2',
324
+ ' exit 1',
325
+ '}',
326
+ 'export GH_TOKEN',
327
+ '',
328
+ `export GIT_AUTHOR_NAME="${config.agent_name}[bot]"`,
329
+ `export GIT_COMMITTER_NAME="${config.agent_name}[bot]"`,
330
+ '',
331
+ `echo "Starting ${config.agent_name} (${config.agent_role})..."`,
332
+ ];
333
+ return assemble(managedHeaderLines('generateEnvGitHub'), body);
334
+ }
335
+ // ---------------------------------------------------------------------------
336
+ // env.certs — macf-managed
337
+ // ---------------------------------------------------------------------------
338
+ /**
339
+ * Generate `.claude/.macf/env.certs` content.
340
+ *
341
+ * CA + agent cert paths + log path. Local-mode CAs live next to the
342
+ * registry file (`~/.macf/registry/<project>.ca.{crt,key}`), GitHub-mode
343
+ * CAs live under `~/.macf/certs/<project>/`. Both modes need
344
+ * MACF_CA_CERT/MACF_CA_KEY exported for channel-server mTLS.
345
+ */
346
+ export function generateEnvCerts(config) {
347
+ const lines = [
348
+ '# Generator: generateEnvCerts (env-files.ts)',
349
+ '',
350
+ ];
351
+ if (isLocalMode(config)) {
352
+ // Pre-resolve the local-registry directory at template time so the
353
+ // generated file doesn't need to expand `~`. Tilde already resolved
354
+ // in cfg.registry.path (init.ts uses os.homedir()).
355
+ const registryPath = config.registry.type === 'local' ? config.registry.path : '';
356
+ const registryDir = posixDirname(registryPath);
357
+ lines.push(`export MACF_CA_CERT="${registryDir}/${config.project}.ca.crt"`, `export MACF_CA_KEY="${registryDir}/${config.project}.ca.key"`);
358
+ }
359
+ else {
360
+ lines.push(`export MACF_CA_CERT="$HOME/.macf/certs/${config.project}/ca-cert.pem"`, `export MACF_CA_KEY="$HOME/.macf/certs/${config.project}/ca-key.pem"`);
361
+ }
362
+ lines.push('export MACF_AGENT_CERT="$SCRIPT_DIR/.macf/certs/agent-cert.pem"', 'export MACF_AGENT_KEY="$SCRIPT_DIR/.macf/certs/agent-key.pem"', 'export MACF_LOG_PATH="$SCRIPT_DIR/.macf/logs/channel.log"');
363
+ return assemble(managedHeaderLines('generateEnvCerts'), lines);
364
+ }
365
+ // ---------------------------------------------------------------------------
366
+ // env.registry — macf-managed
367
+ // ---------------------------------------------------------------------------
368
+ /**
369
+ * Generate `.claude/.macf/env.registry` content.
370
+ *
371
+ * MACF_REGISTRY_TYPE + per-type vars. Mirrors `claude-sh.ts`'s
372
+ * `registryEnvLines` exhaustive switch (so a new RegistryConfig variant
373
+ * fails the build here too, forcing a paired update).
374
+ */
375
+ export function generateEnvRegistry(config) {
376
+ const body = [
377
+ '# Generator: generateEnvRegistry (env-files.ts)',
378
+ '#',
379
+ '# The plugin\'s src/config.ts reads MACF_REGISTRY_TYPE + per-type vars',
380
+ '# on startup; without them the plugin falls back to a hardcoded default',
381
+ '# repo and 403s every registry op on consumers in other scopes.',
382
+ '# See macf#178.',
383
+ '',
384
+ ];
385
+ switch (config.registry.type) {
386
+ case 'repo':
387
+ body.push(`export MACF_REGISTRY_TYPE="repo"`, `export MACF_REGISTRY_REPO="${config.registry.owner}/${config.registry.repo}"`);
388
+ break;
389
+ case 'org':
390
+ body.push(`export MACF_REGISTRY_TYPE="org"`, `export MACF_REGISTRY_ORG="${config.registry.org}"`);
391
+ break;
392
+ case 'profile':
393
+ body.push(`export MACF_REGISTRY_TYPE="profile"`, `export MACF_REGISTRY_USER="${config.registry.user}"`);
394
+ break;
395
+ case 'local':
396
+ body.push(`export MACF_REGISTRY_TYPE="local"`, `export MACF_REGISTRY_PATH="${config.registry.path}"`);
397
+ break;
398
+ }
399
+ return assemble(managedHeaderLines('generateEnvRegistry'), body);
400
+ }
401
+ // ---------------------------------------------------------------------------
402
+ // env.telemetry — operator-managed; MINIMAL when MACF_OTEL_DISABLED=1
403
+ // ---------------------------------------------------------------------------
404
+ /**
405
+ * Generate `.claude/.macf/env.telemetry` content.
406
+ *
407
+ * The 3 OTel mandatory gates + per-signal exporters + endpoint resolution.
408
+ * Mirrors `claude-sh.ts`'s `otelTelemetryLines` (including the same
409
+ * shell-unsafe-char rejection on MACF_OTEL_ENDPOINT and the same default
410
+ * endpoint `http://localhost:14318` per macf-devops-toolkit canonical k3d
411
+ * topology).
412
+ *
413
+ * **Minimal placeholder when `MACF_OTEL_DISABLED=1`** (operator opt-out at
414
+ * template-time per macf#197). Body shrinks to header + schema_version +
415
+ * a comment explaining the opt-out — no OTel exports emitted, so sourcing
416
+ * the file is a no-op.
417
+ *
418
+ * @param env — defaults to `process.env`; tests inject a fake.
419
+ */
420
+ export function generateEnvTelemetry(config, env = process.env) {
421
+ if (env['MACF_OTEL_DISABLED'] === '1' || env['MACF_OTEL_DISABLED'] === 'true') {
422
+ const body = [
423
+ '# Generator: generateEnvTelemetry (env-files.ts)',
424
+ '#',
425
+ '# Telemetry intentionally disabled (MACF_OTEL_DISABLED=1 at',
426
+ '# `macf init` / `macf update` time). No OTel exporters or gates',
427
+ '# are configured; sourcing this file is a no-op. See macf#197.',
428
+ '#',
429
+ '# To re-enable: re-run `macf update` without MACF_OTEL_DISABLED set.',
430
+ ];
431
+ return assemble(operatorHeaderLines('generateEnvTelemetry'), body);
432
+ }
433
+ const endpoint = env['MACF_OTEL_ENDPOINT'] ?? 'http://localhost:14318';
434
+ // Same allowlist pattern as otelTelemetryLines in claude-sh.ts. The
435
+ // endpoint value gets embedded verbatim in a shell double-quoted export.
436
+ if (/["$`\\\n\r]/.test(endpoint)) {
437
+ throw new Error(`MACF_OTEL_ENDPOINT contains a shell-unsafe character. ` +
438
+ `Got: ${JSON.stringify(endpoint)}. ` +
439
+ `Expected a plain URL like http://host:port.`);
440
+ }
441
+ const body = [
442
+ '# Generator: generateEnvTelemetry (env-files.ts)',
443
+ '#',
444
+ '# Claude Code native OTEL telemetry → observability stack',
445
+ '# (macf#197 + macf#245). Three telemetry signal gates — each',
446
+ '# independent, ALL required for the corresponding signal to emit:',
447
+ '# CLAUDE_CODE_ENABLE_TELEMETRY — master telemetry gate',
448
+ '# CLAUDE_CODE_ENHANCED_TELEMETRY_BETA — additional gate for traces',
449
+ '# OTEL_TRACES_EXPORTER=otlp — emit traces',
450
+ '# OTEL_METRICS_EXPORTER=otlp — emit metrics',
451
+ '# OTEL_LOGS_EXPORTER=otlp — emit logs',
452
+ '# Without the per-signal exporter env vars, that signal silently emits',
453
+ '# nothing even if the master gate is on (#245 surfaced the metrics+logs',
454
+ '# gap — only traces had the exporter set; metrics + logs were dark).',
455
+ '#',
456
+ '# Endpoint override has two layers (groundnuty/macf#282):',
457
+ '# - Template-time: MACF_OTEL_ENDPOINT=<url> at `macf init` /',
458
+ '# `macf update` bakes a different default into this file',
459
+ '# - Run-time: OTEL_EXPORTER_OTLP_ENDPOINT=<url> in the shell',
460
+ '# BEFORE invoking ./claude.sh overrides the baked default',
461
+ 'export CLAUDE_CODE_ENABLE_TELEMETRY=1',
462
+ 'export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1',
463
+ 'export OTEL_TRACES_EXPORTER=otlp',
464
+ 'export OTEL_METRICS_EXPORTER=otlp',
465
+ 'export OTEL_LOGS_EXPORTER=otlp',
466
+ // 4-layer endpoint resolution chain (macf#313). Requires
467
+ // macf_settings_get to be defined upstream — env.identity defines it,
468
+ // and PR-B's claude.sh sources env.identity before env.telemetry.
469
+ `MACF_OTEL_ENDPOINT="\${MACF_OTEL_ENDPOINT:-$(macf_settings_get MACF_OTEL_ENDPOINT)}"`,
470
+ `MACF_OTEL_ENDPOINT="\${MACF_OTEL_ENDPOINT:-${endpoint}}"`,
471
+ 'export OTEL_EXPORTER_OTLP_ENDPOINT="${OTEL_EXPORTER_OTLP_ENDPOINT:-$MACF_OTEL_ENDPOINT}"',
472
+ 'export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf',
473
+ `export OTEL_SERVICE_NAME="macf-agent-${config.agent_name}"`,
474
+ `export OTEL_RESOURCE_ATTRIBUTES="gen_ai.agent.name=${config.agent_name},gen_ai.agent.role=${config.agent_role},service.namespace=macf"`,
475
+ ];
476
+ return assemble(operatorHeaderLines('generateEnvTelemetry'), body);
477
+ }
478
+ // ---------------------------------------------------------------------------
479
+ // env.tmux — operator-managed; MINIMAL when neither tmux_session nor _window set
480
+ // ---------------------------------------------------------------------------
481
+ /**
482
+ * Generate `.claude/.macf/env.tmux` content.
483
+ *
484
+ * MACF_TMUX_SESSION + MACF_TMUX_WINDOW for the on-notify wake path
485
+ * (macf#185). When the channel server's onNotify handler fires, it shells
486
+ * out to `tmux-send-to-claude.sh <session>:<window> <prompt>` to inject
487
+ * the notification as the TUI's next input turn.
488
+ *
489
+ * **Minimal placeholder when neither field is set in config.** The wake
490
+ * path falls back to auto-detect from $TMUX in that case (existing
491
+ * behavior — no env var needed). Avoids generating an empty-export file.
492
+ */
493
+ export function generateEnvTmux(config) {
494
+ const hasSession = config.tmux_session !== undefined;
495
+ const hasWindow = config.tmux_window !== undefined;
496
+ if (!hasSession && !hasWindow) {
497
+ const body = [
498
+ '# Generator: generateEnvTmux (env-files.ts)',
499
+ '#',
500
+ '# No MACF_TMUX_SESSION or MACF_TMUX_WINDOW configured for this agent.',
501
+ '# The on-notify wake path (macf#185) auto-detects from $TMUX when the',
502
+ '# channel server is launched inside a tmux pane; otherwise no-ops',
503
+ '# silently. To target a specific named pane (e.g., agent launched',
504
+ '# outside tmux by a supervisor), set tmux_session / tmux_window in',
505
+ '# `.macf/macf-agent.json` and re-run `macf update`.',
506
+ ];
507
+ return assemble(operatorHeaderLines('generateEnvTmux'), body);
508
+ }
509
+ const body = [
510
+ '# Generator: generateEnvTmux (env-files.ts)',
511
+ '#',
512
+ '# tmux session:window for the on-notify wake path (macf#185). The',
513
+ '# channel server\'s onNotify handler shells out to tmux-send-to-claude.sh',
514
+ '# <session>:<window> <prompt> to inject notifications into the TUI.',
515
+ '',
516
+ ];
517
+ if (hasSession) {
518
+ body.push(`export MACF_TMUX_SESSION="${config.tmux_session}"`);
519
+ }
520
+ if (hasWindow) {
521
+ body.push(`export MACF_TMUX_WINDOW="${config.tmux_window}"`);
522
+ }
523
+ return assemble(operatorHeaderLines('generateEnvTmux'), body);
524
+ }
525
+ /**
526
+ * Write all 7 per-concern env files into `<projectDir>/.claude/.macf/`.
527
+ *
528
+ * env._helpers — library file (sourced first per alphabetical order)
529
+ * env.certs — cert + log paths
530
+ * env.github — App creds + GH_TOKEN mint (empty-comment in local-mode)
531
+ * env.identity — MACF_PROJECT / MACF_AGENT_NAME / ROLE / TYPE / WORKSPACE_DIR
532
+ * env.registry — MACF_REGISTRY_TYPE + per-type vars
533
+ * env.telemetry — OTel gates + endpoint (operator-managed)
534
+ * env.tmux — MACF_TMUX_SESSION / MACF_TMUX_WINDOW (operator-managed)
535
+ *
536
+ * **Always emits all 7 files** — even when the concern is empty (e.g.,
537
+ * env.tmux with no tmux_session set, env.github in local-mode). The
538
+ * generators emit a header + comment explaining the empty case. This
539
+ * keeps the on-disk layout uniform across configs, so claude.sh's
540
+ * source-loop pattern (`for f in env.*; do source "$f"; done`) is
541
+ * deterministic — no "did the file get skipped" branching for the
542
+ * sourcing script to consider.
543
+ *
544
+ * **Mode 0644** — these are sourced, not executed; no execute bit needed.
545
+ *
546
+ * **Overwrites unconditionally** — macf-managed files have a managed
547
+ * header warning operators not to edit. Operator-managed files
548
+ * (env.telemetry, env.tmux) get overwritten too in PR-B because PR-C
549
+ * hasn't added the preserve-existing logic yet. Init's contract is
550
+ * "fresh write"; update's preserve-existing contract lands in PR-C.
551
+ *
552
+ * **Creates `.claude/.macf/`** with mkdir -p semantics. The directory
553
+ * may not exist on a fresh init (the `.claude/` parent often does
554
+ * because of `settings.json` / `rules/`, but `.macf/` under it is new
555
+ * to PR-B).
556
+ *
557
+ * @returns Lists of absolute file paths for caller logging. `skipped`
558
+ * is reserved for PR-C and currently always empty.
559
+ */
560
+ export function writeEnvFiles(projectDir, config) {
561
+ const absDir = resolve(projectDir);
562
+ const envDir = join(absDir, '.claude', '.macf');
563
+ mkdirSync(envDir, { recursive: true });
564
+ // Order in this list is purely for `written` array stability — the
565
+ // on-disk filesystem order is what claude.sh's `for f in env.*` glob
566
+ // sees, and shell glob sorts lexicographically (env._helpers first
567
+ // because `_` < lowercase letters in ASCII).
568
+ const files = [
569
+ { name: 'env._helpers', content: generateEnvHelpers() },
570
+ { name: 'env.identity', content: generateEnvIdentity(config) },
571
+ { name: 'env.github', content: generateEnvGitHub(config) },
572
+ { name: 'env.certs', content: generateEnvCerts(config) },
573
+ { name: 'env.registry', content: generateEnvRegistry(config) },
574
+ { name: 'env.telemetry', content: generateEnvTelemetry(config) },
575
+ { name: 'env.tmux', content: generateEnvTmux(config) },
576
+ ];
577
+ const written = [];
578
+ for (const { name, content } of files) {
579
+ const path = join(envDir, name);
580
+ writeFileSync(path, content, { mode: 0o644 });
581
+ written.push(path);
582
+ }
583
+ return { written, skipped: [] };
584
+ }
585
+ //# sourceMappingURL=env-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-files.js","sourceRoot":"","sources":["../../src/cli/env-files.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -147,6 +147,10 @@ What the flags actually control:
147
147
  --yes skip the unified Proceed? prompt; non-interactive bypass
148
148
  --confirm explicit opt-in to the unified preview-then-prompt flow
149
149
  (also the default for bare \`macf update\`; --yes overrides)
150
+ --no-migrate-env-files
151
+ skip the macf#342 monolithic→multi-file claude.sh migration
152
+ AND env-file refresh (operator opt-out for hand-modified
153
+ launchers; does NOT roll back already-migrated workspaces)
150
154
  --dry-run show diff + would-bump list, write nothing
151
155
 
152
156
  Implication for reproducible bootstrap (cv-e2e-test, harness pinning, etc.):
@@ -161,9 +165,15 @@ Implication for reproducible bootstrap (cv-e2e-test, harness pinning, etc.):
161
165
  .option('--actions', 'Bump only the macf-actions version pin', false)
162
166
  .option('--yes', 'Skip the unified Proceed? prompt; non-interactive bypass', false)
163
167
  .option('--confirm', 'Explicit opt-in to the unified preview-then-prompt flow (also the default; --yes overrides)', false)
168
+ .option('--no-migrate-env-files', 'Skip the macf#342 monolithic→multi-file claude.sh migration AND env-file refresh (operator opt-out)', false)
164
169
  .option('--dry-run', 'Show the diff but do not write the config', false)
165
170
  .option('--dir <path>', 'Project directory (defaults to auto-discovery from cwd)')
166
171
  .action(async (opts) => {
172
+ // Commander's --no-<flag> convention sets opts.migrateEnvFiles = false
173
+ // when --no-migrate-env-files is passed (the default is `true` because
174
+ // of the leading `--no-` in the option name). Translate to the more
175
+ // intuitive opt-out shape used inside `update()`.
176
+ const noMigrateEnvFiles = opts.migrateEnvFiles === false;
167
177
  const code = await update(resolveProjectDir(opts.dir), {
168
178
  all: opts.all,
169
179
  cli: opts.cli,
@@ -172,6 +182,7 @@ Implication for reproducible bootstrap (cv-e2e-test, harness pinning, etc.):
172
182
  yes: opts.yes,
173
183
  dryRun: opts.dryRun,
174
184
  confirm: opts.confirm,
185
+ noMigrateEnvFiles,
175
186
  });
176
187
  process.exitCode = code;
177
188
  });