@triflux/remote 10.50.2 → 10.52.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.
Files changed (134) hide show
  1. package/cto/brief.mjs +0 -20
  2. package/cto/collect.mjs +1 -200
  3. package/cto/current.schema.json +2 -23
  4. package/cto/hygiene.mjs +2 -61
  5. package/cto/lake-root.mjs +0 -56
  6. package/cto/status.mjs +5 -187
  7. package/hub/index.mjs +0 -17
  8. package/hub/team/agent-map.json +0 -1
  9. package/hub/team/agents-row-attach.py +120 -0
  10. package/hub/team/agents-row.mjs +200 -0
  11. package/hub/team/ansi.mjs +1 -35
  12. package/hub/team/backend.mjs +9 -145
  13. package/hub/team/claude-daemon-control.mjs +1 -2
  14. package/hub/team/claude-session-projection.mjs +5 -73
  15. package/hub/team/cli/commands/attach.mjs +1 -17
  16. package/hub/team/cli/commands/control.mjs +12 -38
  17. package/hub/team/cli/commands/debug.mjs +2 -39
  18. package/hub/team/cli/commands/focus.mjs +5 -31
  19. package/hub/team/cli/commands/interrupt.mjs +6 -25
  20. package/hub/team/cli/commands/kill.mjs +1 -30
  21. package/hub/team/cli/commands/list.mjs +0 -16
  22. package/hub/team/cli/commands/send.mjs +6 -26
  23. package/hub/team/cli/commands/start/index.mjs +42 -133
  24. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  25. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  26. package/hub/team/cli/commands/start/start-mux.mjs +2 -7
  27. package/hub/team/cli/commands/status.mjs +2 -57
  28. package/hub/team/cli/commands/stop.mjs +14 -19
  29. package/hub/team/cli/help.mjs +4 -5
  30. package/hub/team/cli/manifest.mjs +5 -1
  31. package/hub/team/cli/render.mjs +0 -12
  32. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  33. package/hub/team/cli/services/state-store.mjs +5 -20
  34. package/hub/team/handoff.mjs +3 -34
  35. package/hub/team/headless.mjs +108 -1296
  36. package/hub/team/orchestrator.mjs +4 -30
  37. package/hub/team/pane.mjs +6 -28
  38. package/hub/team/psmux.mjs +3 -34
  39. package/hub/team/remote-session.mjs +1 -2
  40. package/hub/team/session-context.mjs +1 -1
  41. package/hub/team/session.mjs +7 -91
  42. package/hub/team/tui-core.mjs +1 -1
  43. package/hub/team/tui-viewer.mjs +11 -58
  44. package/hub/team/tui.mjs +8 -306
  45. package/hub/team/uds-orchestrator.mjs +2 -580
  46. package/hub/team/wt-manager.mjs +2 -2
  47. package/hub/workers/factory.mjs +6 -84
  48. package/hub/workers/interface.mjs +1 -7
  49. package/hub/workers/lib/jsonrpc-core.mjs +11 -0
  50. package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
  51. package/package.json +3 -8
  52. package/scripts/lib/agent-route-policy.mjs +143 -62
  53. package/scripts/lib/cli-claude.mjs +0 -14
  54. package/scripts/lib/env-probe.mjs +2 -131
  55. package/scripts/lib/gemini-profiles.mjs +2 -69
  56. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  57. package/scripts/lib/machine-profile.mjs +3 -17
  58. package/scripts/lib/mcp-filter.mjs +109 -53
  59. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  60. package/scripts/lib/mcp-health.mjs +2 -2
  61. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  62. package/scripts/lib/psmux-info.mjs +11 -59
  63. package/scripts/lib/skill-template.mjs +0 -245
  64. package/hub/hub-lifecycle.mjs +0 -248
  65. package/hub/pipe.mjs +0 -825
  66. package/hub/promote-penalties.mjs +0 -138
  67. package/hub/public/dashboard.html +0 -416
  68. package/hub/schema.sql +0 -209
  69. package/hub/server.mjs +0 -3048
  70. package/hub/store-adapter.mjs +0 -182
  71. package/hub/store.mjs +0 -1736
  72. package/hub/team/build-worker-prompt.mjs +0 -92
  73. package/hub/team/check-mcp-hub.mjs +0 -49
  74. package/hub/team/claude-native-bridge.mjs +0 -987
  75. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  76. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  77. package/hub/team/cli/services/hub-client.mjs +0 -264
  78. package/hub/team/cli/services/native-control.mjs +0 -145
  79. package/hub/team/codex-review.mjs +0 -520
  80. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  81. package/hub/team/conductor-registry.mjs +0 -121
  82. package/hub/team/conductor.mjs +0 -1529
  83. package/hub/team/consensus-meta.mjs +0 -176
  84. package/hub/team/cto-auto-collect.mjs +0 -115
  85. package/hub/team/cto-pull.mjs +0 -31
  86. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  87. package/hub/team/dashboard.mjs +0 -322
  88. package/hub/team/event-log.mjs +0 -84
  89. package/hub/team/execution-mode.mjs +0 -324
  90. package/hub/team/extract-completion-payload.mjs +0 -110
  91. package/hub/team/headless-bridge-session.mjs +0 -17
  92. package/hub/team/health-probe.mjs +0 -435
  93. package/hub/team/interactive-native-launcher.mjs +0 -119
  94. package/hub/team/interactive-native-worker.mjs +0 -80
  95. package/hub/team/interactive-tui-transport.mjs +0 -479
  96. package/hub/team/launcher-template.mjs +0 -144
  97. package/hub/team/lead-control.mjs +0 -191
  98. package/hub/team/mcp-selector.mjs +0 -145
  99. package/hub/team/native-supervisor.mjs +0 -460
  100. package/hub/team/native.mjs +0 -727
  101. package/hub/team/recovery-store.mjs +0 -149
  102. package/hub/team/remote-probe.mjs +0 -292
  103. package/hub/team/remote-watcher.mjs +0 -506
  104. package/hub/team/runtime-strategy.mjs +0 -200
  105. package/hub/team/schemas/worker-completion.json +0 -39
  106. package/hub/team/sentinel-capture.mjs +0 -127
  107. package/hub/team/session-sync.mjs +0 -316
  108. package/hub/team/swarm-cli.mjs +0 -390
  109. package/hub/team/swarm-hypervisor.mjs +0 -2206
  110. package/hub/team/swarm-intent.mjs +0 -216
  111. package/hub/team/swarm-locks.mjs +0 -335
  112. package/hub/team/swarm-planner.mjs +0 -371
  113. package/hub/team/swarm-preflight.mjs +0 -366
  114. package/hub/team/swarm-reconciler.mjs +0 -242
  115. package/hub/team/synapse-cli.mjs +0 -458
  116. package/hub/team/synapse-http.mjs +0 -311
  117. package/hub/team/synapse-registry.mjs +0 -618
  118. package/hub/team/tui-remote-adapter.mjs +0 -377
  119. package/hub/team/tui-synapse.mjs +0 -223
  120. package/hub/team/worker-completion-validator.mjs +0 -79
  121. package/hub/team/worker-sandbox.mjs +0 -125
  122. package/hub/team/worktree-lifecycle.mjs +0 -639
  123. package/hub/team/wt-templates.json +0 -43
  124. package/hub/tools.mjs +0 -900
  125. package/hub/workers/antigravity-route-worker.mjs +0 -179
  126. package/hub/workers/codex-app-server-worker.mjs +0 -1067
  127. package/hub/workers/delegator-mcp.mjs +0 -1098
  128. package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
  129. package/scripts/lib/claudemd-scanner.mjs +0 -223
  130. package/scripts/lib/context.mjs +0 -67
  131. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  132. package/scripts/lib/handoff.mjs +0 -187
  133. package/scripts/lib/logger.mjs +0 -122
  134. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,458 +0,0 @@
1
- // hub/team/synapse-cli.mjs — CLI surface for Synapse v1.
2
- // Reads persisted registry state + git log for the "status" and "why" commands.
3
- // Hub integration comes later; for v1 we go straight to the JSON persist files
4
- // so the CLI works even when Hub is offline.
5
-
6
- import { execFile } from "node:child_process";
7
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
8
- import { homedir } from "node:os";
9
- import { join, resolve } from "node:path";
10
-
11
- import { parseIntentTrailer } from "./swarm-intent.mjs";
12
-
13
- const DEFAULT_REGISTRY_CANDIDATES = [
14
- ".triflux/synapse-registry.json",
15
- ".triflux/synapse/registry.json",
16
- join(homedir(), ".claude", "cache", "tfx-hub", "synapse-registry.json"),
17
- ];
18
- const DEFAULT_SWARM_LOGS_DIR = ".triflux/swarm-logs";
19
- const STALE_AFTER_MS = 30 * 60 * 1000;
20
- const STATUS_PRIORITY = {
21
- active: 3,
22
- running: 3,
23
- completed: 2,
24
- failed: 2,
25
- stale: 1,
26
- };
27
-
28
- function gitExec(args, cwd) {
29
- return new Promise((res, rej) => {
30
- execFile(
31
- "git",
32
- args,
33
- { cwd, windowsHide: true, timeout: 15_000 },
34
- (err, stdout) => {
35
- if (err) rej(err);
36
- else res(stdout);
37
- },
38
- );
39
- });
40
- }
41
-
42
- function locateRegistryPath(explicit) {
43
- if (explicit && existsSync(explicit)) return explicit;
44
- for (const candidate of DEFAULT_REGISTRY_CANDIDATES) {
45
- if (existsSync(candidate)) return candidate;
46
- }
47
- return null;
48
- }
49
-
50
- function loadRegistrySnapshot(path) {
51
- if (!path) return [];
52
- try {
53
- const raw = readFileSync(path, "utf8");
54
- const parsed = JSON.parse(raw);
55
- if (Array.isArray(parsed)) return parsed;
56
- if (parsed && typeof parsed === "object") {
57
- // Accept both array-form and object-form persist layouts.
58
- if (Array.isArray(parsed.sessions)) return parsed.sessions;
59
- return Object.values(parsed);
60
- }
61
- return [];
62
- } catch {
63
- return [];
64
- }
65
- }
66
-
67
- function parseJsonLines(path) {
68
- try {
69
- return readFileSync(path, "utf8")
70
- .split(/\r?\n/)
71
- .map((line) => line.trim())
72
- .filter(Boolean)
73
- .flatMap((line) => {
74
- try {
75
- return [JSON.parse(line)];
76
- } catch {
77
- return [];
78
- }
79
- });
80
- } catch {
81
- return [];
82
- }
83
- }
84
-
85
- function normalizeRunId(name) {
86
- return String(name || "").replace(/^run-/, "");
87
- }
88
-
89
- function eventTimeMs(event) {
90
- const ms = Date.parse(String(event?.ts || ""));
91
- return Number.isFinite(ms) ? ms : 0;
92
- }
93
-
94
- function deriveLogStatus(events, lastHeartbeatMs, nowMs) {
95
- const stateEvents = events.filter((e) => e.event === "swarm_state");
96
- const lastState = stateEvents[stateEvents.length - 1]?.to;
97
- if (lastState === "completed" || lastState === "failed") return lastState;
98
- if (events.some((e) => e.event === "integration_complete")) {
99
- const lastIntegration = [...events]
100
- .reverse()
101
- .find((e) => e.event === "integration_complete");
102
- if (
103
- Array.isArray(lastIntegration?.failed) &&
104
- lastIntegration.failed.length
105
- ) {
106
- return "failed";
107
- }
108
- return "completed";
109
- }
110
- if (nowMs - lastHeartbeatMs > STALE_AFTER_MS) return "stale";
111
- return "active";
112
- }
113
-
114
- function buildLogSession(runDir, nowMs = Date.now()) {
115
- const eventPath = join(runDir, "swarm-events.jsonl");
116
- if (!existsSync(eventPath)) return null;
117
- const events = parseJsonLines(eventPath);
118
- if (events.length === 0) return null;
119
-
120
- let statMs = 0;
121
- try {
122
- statMs = statSync(eventPath).mtimeMs;
123
- } catch {}
124
-
125
- const eventMs = events.map(eventTimeMs).filter((ms) => ms > 0);
126
- const lastEventMs = eventMs.length ? Math.max(...eventMs) : statMs;
127
- const launched = new Set();
128
- const completed = new Set();
129
- const failed = new Set();
130
- for (const event of events) {
131
- if (event.event === "shard_launched" && event.shard) {
132
- launched.add(event.shard);
133
- } else if (event.event === "shard_completed" && event.shard) {
134
- completed.add(event.shard);
135
- } else if (
136
- (event.event === "shard_failed" ||
137
- event.event === "shard_launch_failed") &&
138
- event.shard
139
- ) {
140
- failed.add(event.shard);
141
- }
142
- }
143
-
144
- const runId = normalizeRunId(runDir.split(/[\\/]/).pop());
145
- const totalShards = launched.size || completed.size + failed.size;
146
- const aliveWorkers = [...launched].filter(
147
- (name) => !completed.has(name) && !failed.has(name),
148
- ).length;
149
-
150
- return {
151
- sessionId: runId,
152
- runId,
153
- host: "-",
154
- branch: "-",
155
- dirtyFiles: [],
156
- status: deriveLogStatus(events, lastEventMs || nowMs, nowMs),
157
- taskSummary: "swarm log run",
158
- source: "logs",
159
- shards: totalShards ? `${completed.size}/${totalShards}` : "-",
160
- workers: totalShards ? `${aliveWorkers} alive` : "-",
161
- lastHeartbeat: lastEventMs || null,
162
- logDir: runDir,
163
- };
164
- }
165
-
166
- export function loadSwarmLogSessions(logsDir, opts = {}) {
167
- const nowMs = opts.nowMs ?? Date.now();
168
- const root = logsDir || DEFAULT_SWARM_LOGS_DIR;
169
- if (!existsSync(root)) return [];
170
- try {
171
- return readdirSync(root, { withFileTypes: true })
172
- .filter((entry) => entry.isDirectory() && entry.name.startsWith("run-"))
173
- .map((entry) => buildLogSession(join(root, entry.name), nowMs))
174
- .filter(Boolean);
175
- } catch {
176
- return [];
177
- }
178
- }
179
-
180
- function statusPriority(status) {
181
- return STATUS_PRIORITY[status] || 0;
182
- }
183
-
184
- function mergeSession(existing, next) {
185
- const status =
186
- statusPriority(next.status) > statusPriority(existing.status)
187
- ? next.status
188
- : existing.status;
189
- const source =
190
- existing.source && next.source && existing.source !== next.source
191
- ? "synapse + logs"
192
- : existing.source || next.source || "synapse";
193
- return {
194
- ...next,
195
- ...existing,
196
- status,
197
- source,
198
- shards: next.shards || existing.shards,
199
- workers: next.workers || existing.workers,
200
- lastHeartbeat: Math.max(
201
- Number(existing.lastHeartbeat || 0),
202
- Number(next.lastHeartbeat || 0),
203
- ),
204
- logDir: existing.logDir || next.logDir,
205
- };
206
- }
207
-
208
- export function mergeRegistryAndLogSessions(registrySessions, logSessions) {
209
- const merged = new Map();
210
- for (const session of registrySessions) {
211
- const key = String(session.runId || session.sessionId || "");
212
- if (!key) continue;
213
- merged.set(key, {
214
- ...session,
215
- source: session.source || "synapse",
216
- lastHeartbeat: session.lastHeartbeat || null,
217
- });
218
- }
219
- for (const session of logSessions) {
220
- const key = String(session.runId || session.sessionId || "");
221
- if (!key) continue;
222
- const existing = merged.get(key);
223
- merged.set(key, existing ? mergeSession(existing, session) : session);
224
- }
225
- return [...merged.values()].sort((left, right) => {
226
- const rightTs = Number(right.lastHeartbeat || 0);
227
- const leftTs = Number(left.lastHeartbeat || 0);
228
- return rightTs - leftTs;
229
- });
230
- }
231
-
232
- function formatAge(ts, nowMs = Date.now()) {
233
- if (!Number.isFinite(Number(ts)) || Number(ts) <= 0) return "-";
234
- const seconds = Math.max(0, Math.round((nowMs - Number(ts)) / 1000));
235
- if (seconds < 60) return `${seconds}s ago`;
236
- const minutes = Math.round(seconds / 60);
237
- if (minutes < 60) return `${minutes}m ago`;
238
- const hours = Math.round(minutes / 60);
239
- return `${hours}h ago`;
240
- }
241
-
242
- function hasSwarmLogFields(sessions) {
243
- return sessions.some(
244
- (s) =>
245
- s.source === "logs" ||
246
- s.source === "synapse + logs" ||
247
- s.runId ||
248
- s.shards ||
249
- s.workers,
250
- );
251
- }
252
-
253
- function formatSwarmStatus(sessions) {
254
- const nowMs = Date.now();
255
- const rows = [];
256
- rows.push(
257
- "RUN_ID STATUS SHARDS WORKERS LAST_HEARTBEAT SOURCE",
258
- );
259
- rows.push(
260
- "───────────────────── ───────── ─────── ─────── ────────────── ──────────────",
261
- );
262
- for (const s of sessions) {
263
- const id = String(s.runId || s.sessionId || "?")
264
- .padEnd(21)
265
- .slice(0, 21);
266
- const status = String(s.status || "active")
267
- .padEnd(9)
268
- .slice(0, 9);
269
- const shards = String(s.shards || "-")
270
- .padEnd(7)
271
- .slice(0, 7);
272
- const workers = String(s.workers || "-")
273
- .padEnd(7)
274
- .slice(0, 7);
275
- const heartbeat = formatAge(s.lastHeartbeat, nowMs).padEnd(14).slice(0, 14);
276
- const source = String(s.source || "synapse").slice(0, 20);
277
- rows.push(
278
- `${id} ${status} ${shards} ${workers} ${heartbeat} ${source}`,
279
- );
280
- }
281
- return rows.join("\n");
282
- }
283
-
284
- function formatStatus(sessions) {
285
- if (!sessions.length) {
286
- return "no active sessions (synapse-registry empty)";
287
- }
288
- if (hasSwarmLogFields(sessions)) return formatSwarmStatus(sessions);
289
- const rows = [];
290
- rows.push("SESSION HOST BRANCH DIRTY STATE TASK");
291
- rows.push(
292
- "───────────────────── ────── ──────── ────── ─────── ─────────────",
293
- );
294
- for (const s of sessions) {
295
- const id = (s.sessionId || "?").padEnd(21).slice(0, 21);
296
- const host = (s.host || "?").padEnd(6).slice(0, 6);
297
- const branch = (s.branch || "?").padEnd(8).slice(0, 8);
298
- const dirty = String((s.dirtyFiles || []).length)
299
- .padEnd(6)
300
- .slice(0, 6);
301
- const state = (s.status || "active").padEnd(7).slice(0, 7);
302
- const task = (s.taskSummary || "").slice(0, 40);
303
- rows.push(`${id} ${host} ${branch} ${dirty} ${state} ${task}`);
304
- }
305
- return rows.join("\n");
306
- }
307
-
308
- /**
309
- * `tfx synapse status` — list active swarm sessions from persisted registry.
310
- */
311
- export async function cmdSynapseStatus(args = [], opts = {}) {
312
- const jsonOut = args.includes("--json") || opts.json;
313
- const explicit = extractFlag(args, "--registry");
314
- const explicitLogsDir = extractFlag(args, "--logs-dir");
315
- const path = locateRegistryPath(explicit);
316
- const registrySessions = loadRegistrySnapshot(path);
317
- const logsDir = explicitLogsDir || DEFAULT_SWARM_LOGS_DIR;
318
- const logSessions = loadSwarmLogSessions(logsDir);
319
- const sessions = mergeRegistryAndLogSessions(registrySessions, logSessions);
320
-
321
- if (jsonOut) {
322
- process.stdout.write(
323
- JSON.stringify(
324
- {
325
- registry: path,
326
- logsDir: existsSync(logsDir) ? logsDir : null,
327
- count: sessions.length,
328
- sessions,
329
- },
330
- null,
331
- 2,
332
- ) + "\n",
333
- );
334
- return;
335
- }
336
-
337
- if (!path && logSessions.length === 0) {
338
- process.stdout.write(
339
- "no registry or swarm logs found (looked for .triflux/synapse-registry.json and .triflux/swarm-logs)\n",
340
- );
341
- return;
342
- }
343
- if (path) process.stdout.write(`registry: ${path}\n`);
344
- if (logSessions.length > 0) process.stdout.write(`swarm logs: ${logsDir}\n`);
345
- process.stdout.write(`${formatStatus(sessions)}\n`);
346
- }
347
-
348
- function extractFlag(args, name) {
349
- const idx = args.indexOf(name);
350
- if (idx < 0) return null;
351
- return args[idx + 1] || null;
352
- }
353
-
354
- async function resolveCommitForPath(path, cwd) {
355
- try {
356
- const out = await gitExec(["log", "-1", "--format=%H", "--", path], cwd);
357
- return out.trim();
358
- } catch {
359
- return "";
360
- }
361
- }
362
-
363
- async function readCommitMessage(sha, cwd) {
364
- try {
365
- return await gitExec(["log", "-1", "--format=%B", sha], cwd);
366
- } catch {
367
- return "";
368
- }
369
- }
370
-
371
- async function readCommitMeta(sha, cwd) {
372
- try {
373
- const out = await gitExec(
374
- ["log", "-1", "--format=%h%x1f%an%x1f%ad%x1f%s", "--date=iso", sha],
375
- cwd,
376
- );
377
- const [short, author, date, subject] = out.trim().split("\x1f");
378
- return { short, author, date, subject };
379
- } catch {
380
- return null;
381
- }
382
- }
383
-
384
- /**
385
- * `tfx why <path>` — show the X-Intent of the last commit touching <path>.
386
- */
387
- export async function cmdSynapseWhy(args = [], opts = {}) {
388
- const jsonOut = args.includes("--json") || opts.json;
389
- const cwd = process.cwd();
390
- const positional = args.filter((a) => !a.startsWith("--"));
391
- const target = positional[0];
392
-
393
- if (!target) {
394
- const err = { error: "path required", usage: "tfx why <path>" };
395
- if (jsonOut) process.stdout.write(JSON.stringify(err) + "\n");
396
- else process.stderr.write("tfx why <path> — path 인자 필요\n");
397
- process.exitCode = 1;
398
- return;
399
- }
400
-
401
- const resolved = resolve(cwd, target);
402
- if (!existsSync(resolved)) {
403
- const err = { error: "path not found", path: target };
404
- if (jsonOut) process.stdout.write(JSON.stringify(err) + "\n");
405
- else process.stderr.write(`tfx why: 파일 없음: ${target}\n`);
406
- process.exitCode = 1;
407
- return;
408
- }
409
-
410
- const sha = await resolveCommitForPath(target, cwd);
411
- if (!sha) {
412
- const result = { path: target, commit: null, intent: null };
413
- if (jsonOut) process.stdout.write(JSON.stringify(result) + "\n");
414
- else process.stdout.write(`no commit touches ${target}\n`);
415
- return;
416
- }
417
-
418
- const [message, meta] = await Promise.all([
419
- readCommitMessage(sha, cwd),
420
- readCommitMeta(sha, cwd),
421
- ]);
422
- const intent = parseIntentTrailer(message);
423
-
424
- if (jsonOut) {
425
- process.stdout.write(
426
- JSON.stringify(
427
- { path: target, commit: { sha, ...meta }, intent },
428
- null,
429
- 2,
430
- ) + "\n",
431
- );
432
- return;
433
- }
434
-
435
- const header = meta
436
- ? `${meta.short} ${meta.author} ${meta.date} ${meta.subject}`
437
- : sha;
438
- process.stdout.write(`${target}\n`);
439
- process.stdout.write(` last commit: ${header}\n`);
440
- if (intent) {
441
- process.stdout.write(` intent.scope: ${intent.scope || "-"}\n`);
442
- process.stdout.write(` intent.action: ${intent.action || "-"}\n`);
443
- process.stdout.write(` intent.reason: ${intent.reason || "-"}\n`);
444
- if (Array.isArray(intent.touches) && intent.touches.length) {
445
- process.stdout.write(
446
- ` intent.touches: ${intent.touches.join(", ")}\n`,
447
- );
448
- }
449
- if (intent.invariant) {
450
- process.stdout.write(` intent.invariant: ${intent.invariant}\n`);
451
- }
452
- if (intent.conflictsWith) {
453
- process.stdout.write(` intent.conflicts: ${intent.conflictsWith}\n`);
454
- }
455
- } else {
456
- process.stdout.write(" intent: (no X-Intent trailer)\n");
457
- }
458
- }