@glasstrace/sdk 1.10.1 → 1.11.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 (39) hide show
  1. package/README.md +43 -5
  2. package/dist/{chunk-WQF7ZQOM.js → chunk-DQFGNX3H.js} +13 -8
  3. package/dist/{chunk-WQF7ZQOM.js.map → chunk-DQFGNX3H.js.map} +1 -1
  4. package/dist/{chunk-UMGZJYC4.js → chunk-FQ4SEG6Y.js} +8 -3
  5. package/dist/chunk-FQ4SEG6Y.js.map +1 -0
  6. package/dist/chunk-KOYZJN6G.js +651 -0
  7. package/dist/chunk-KOYZJN6G.js.map +1 -0
  8. package/dist/{chunk-ZBQQXVHD.js → chunk-YIEXKQYP.js} +2 -67
  9. package/dist/chunk-YIEXKQYP.js.map +1 -0
  10. package/dist/cli/init.cjs +460 -127
  11. package/dist/cli/init.cjs.map +1 -1
  12. package/dist/cli/init.js +29 -16
  13. package/dist/cli/init.js.map +1 -1
  14. package/dist/cli/mcp-add.cjs +346 -98
  15. package/dist/cli/mcp-add.cjs.map +1 -1
  16. package/dist/cli/mcp-add.js +32 -14
  17. package/dist/cli/mcp-add.js.map +1 -1
  18. package/dist/cli/status.cjs +6 -1
  19. package/dist/cli/status.cjs.map +1 -1
  20. package/dist/cli/status.js +7 -2
  21. package/dist/cli/status.js.map +1 -1
  22. package/dist/cli/uninit.cjs +6 -1
  23. package/dist/cli/uninit.cjs.map +1 -1
  24. package/dist/cli/uninit.js +2 -2
  25. package/dist/cli/upgrade-instructions.cjs +390 -113
  26. package/dist/cli/upgrade-instructions.cjs.map +1 -1
  27. package/dist/cli/upgrade-instructions.js +70 -18
  28. package/dist/cli/upgrade-instructions.js.map +1 -1
  29. package/dist/index.cjs +11 -6
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +2 -2
  32. package/dist/node-entry.cjs +11 -6
  33. package/dist/node-entry.cjs.map +1 -1
  34. package/dist/node-entry.js +2 -2
  35. package/package.json +1 -1
  36. package/dist/chunk-TJ46YOGJ.js +0 -355
  37. package/dist/chunk-TJ46YOGJ.js.map +0 -1
  38. package/dist/chunk-UMGZJYC4.js.map +0 -1
  39. package/dist/chunk-ZBQQXVHD.js.map +0 -1
@@ -0,0 +1,651 @@
1
+ import {
2
+ findMarkerBoundaries
3
+ } from "./chunk-YIEXKQYP.js";
4
+
5
+ // src/agent-detection/detect.ts
6
+ import { execFile } from "node:child_process";
7
+ import { access, stat } from "node:fs/promises";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { homedir } from "node:os";
10
+ import { constants } from "node:fs";
11
+ var AGENT_RULES = [
12
+ {
13
+ name: "claude",
14
+ markers: [".claude", "CLAUDE.md"],
15
+ mcpConfigPath: (dir) => join(dir, ".mcp.json"),
16
+ infoFilePath: (dir) => join(dir, "CLAUDE.md"),
17
+ cliBinary: "claude",
18
+ registrationCommand: "npx glasstrace mcp add --agent claude"
19
+ },
20
+ {
21
+ name: "codex",
22
+ // Codex 2026 default discovery is `AGENTS.override.md` → `AGENTS.md` →
23
+ // opt-in `project_doc_fallback_filenames`; `codex.md` is NOT in the
24
+ // default fallback list. Detection requires Codex-specific markers
25
+ // (`codex.md` legacy, `.codex/` config dir) — `AGENTS.md` is NOT
26
+ // included as a marker because the SDK now writes `AGENTS.md`
27
+ // broadly via the multi-target dispatcher's companion writes; if
28
+ // `AGENTS.md` were a Codex marker, every project with the SDK's
29
+ // own companion AGENTS.md would re-classify as Codex on subsequent
30
+ // detect runs and trigger unintended `.codex/config.toml` writes
31
+ // (Codex P1 + Copilot P1 review of Wave 18 PR #274). The canonical
32
+ // write destination remains AGENTS.md regardless of which marker
33
+ // classified the project.
34
+ markers: ["codex.md", ".codex"],
35
+ mcpConfigPath: (dir) => join(dir, ".codex", "config.toml"),
36
+ infoFilePath: (dir) => join(dir, "AGENTS.md"),
37
+ cliBinary: "codex",
38
+ registrationCommand: "npx glasstrace mcp add --agent codex"
39
+ },
40
+ {
41
+ name: "gemini",
42
+ markers: [".gemini", "GEMINI.md"],
43
+ mcpConfigPath: (dir) => join(dir, ".gemini", "settings.json"),
44
+ infoFilePath: (dir) => join(dir, "GEMINI.md"),
45
+ cliBinary: "gemini",
46
+ registrationCommand: "npx glasstrace mcp add --agent gemini"
47
+ },
48
+ {
49
+ name: "cursor",
50
+ // `.cursor/rules/*.mdc` is the current canonical format per Cursor's
51
+ // 2026 docs. `.cursorrules` (single file) is supported-but-deprecated
52
+ // and stays as a transitional fallback that the multi-target write
53
+ // helper writes unconditionally alongside the .mdc canonical.
54
+ markers: [".cursor", ".cursorrules"],
55
+ mcpConfigPath: (dir) => join(dir, ".cursor", "mcp.json"),
56
+ infoFilePath: (dir) => join(dir, ".cursor", "rules", "glasstrace.mdc"),
57
+ cliBinary: null,
58
+ registrationCommand: "npx glasstrace mcp add --agent cursor"
59
+ },
60
+ {
61
+ name: "windsurf",
62
+ // Windsurf's current canonical workspace-rules format is
63
+ // `.windsurf/rules/*.md`. AGENTS.md is a parallel cross-tool
64
+ // mechanism Windsurf also reads BUT is NOT included as a Windsurf
65
+ // detection marker — the SDK writes `AGENTS.md` broadly via the
66
+ // multi-target dispatcher's companion writes, so treating
67
+ // `AGENTS.md` as a Windsurf marker would re-classify every
68
+ // SDK-managed project as Windsurf and cause `glasstrace uninit`
69
+ // to mutate the global `~/.codeium/windsurf/mcp_config.json` for
70
+ // non-Windsurf projects (Codex P1 + Copilot P1 review of Wave 18
71
+ // PR #274). The single-file `.windsurfrules` is the deprecated
72
+ // legacy form — recognized as a marker so legacy projects classify
73
+ // correctly, but the SDK no longer writes to it.
74
+ markers: [".windsurf", ".windsurfrules"],
75
+ mcpConfigPath: () => join(homedir(), ".codeium", "windsurf", "mcp_config.json"),
76
+ infoFilePath: (dir) => join(dir, ".windsurf", "rules", "glasstrace.md"),
77
+ cliBinary: null,
78
+ registrationCommand: "npx glasstrace mcp add --agent windsurf"
79
+ }
80
+ ];
81
+ async function pathExists(path, mode = constants.R_OK) {
82
+ try {
83
+ await access(path, mode);
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+ async function findGitRoot(startDir) {
90
+ let current = resolve(startDir);
91
+ while (true) {
92
+ if (await pathExists(join(current, ".git"), constants.F_OK)) {
93
+ return current;
94
+ }
95
+ const parent = dirname(current);
96
+ if (parent === current) {
97
+ break;
98
+ }
99
+ current = parent;
100
+ }
101
+ return resolve(startDir);
102
+ }
103
+ function isCliAvailable(binary) {
104
+ return new Promise((resolve2) => {
105
+ const command = process.platform === "win32" ? "where" : "which";
106
+ execFile(command, [binary], (error) => {
107
+ resolve2(error === null);
108
+ });
109
+ });
110
+ }
111
+ async function detectAgents(projectRoot) {
112
+ const resolvedRoot = resolve(projectRoot);
113
+ let rootStat;
114
+ try {
115
+ rootStat = await stat(resolvedRoot);
116
+ } catch (err) {
117
+ const code = err.code;
118
+ throw new Error(
119
+ `projectRoot does not exist: ${resolvedRoot}` + (code ? ` (${code})` : "")
120
+ );
121
+ }
122
+ if (!rootStat.isDirectory()) {
123
+ throw new Error(`projectRoot is not a directory: ${resolvedRoot}`);
124
+ }
125
+ const gitRoot = await findGitRoot(resolvedRoot);
126
+ const searchDirs = [];
127
+ let current = resolvedRoot;
128
+ while (true) {
129
+ searchDirs.push(current);
130
+ if (current === gitRoot) {
131
+ break;
132
+ }
133
+ const parent = dirname(current);
134
+ if (parent === current) {
135
+ break;
136
+ }
137
+ current = parent;
138
+ }
139
+ const detected = [];
140
+ const seenAgents = /* @__PURE__ */ new Set();
141
+ for (const rule of AGENT_RULES) {
142
+ let foundDir = null;
143
+ for (const dir of searchDirs) {
144
+ let markerFound = false;
145
+ for (const marker of rule.markers) {
146
+ if (await pathExists(join(dir, marker))) {
147
+ markerFound = true;
148
+ break;
149
+ }
150
+ }
151
+ if (markerFound) {
152
+ foundDir = dir;
153
+ break;
154
+ }
155
+ }
156
+ if (foundDir === null) {
157
+ continue;
158
+ }
159
+ if (seenAgents.has(rule.name)) {
160
+ continue;
161
+ }
162
+ seenAgents.add(rule.name);
163
+ const infoFilePath = rule.infoFilePath(foundDir);
164
+ const cliAvailable = rule.cliBinary ? await isCliAvailable(rule.cliBinary) : false;
165
+ detected.push({
166
+ name: rule.name,
167
+ mcpConfigPath: rule.mcpConfigPath(foundDir),
168
+ infoFilePath,
169
+ cliAvailable,
170
+ registrationCommand: rule.registrationCommand
171
+ });
172
+ }
173
+ detected.push({
174
+ name: "generic",
175
+ mcpConfigPath: join(resolvedRoot, ".glasstrace", "mcp.json"),
176
+ infoFilePath: join(resolvedRoot, "AGENTS.md"),
177
+ cliAvailable: false,
178
+ registrationCommand: null
179
+ });
180
+ return detected;
181
+ }
182
+
183
+ // src/agent-detection/agent-instruction-text.ts
184
+ function buildAgentInstructionBody() {
185
+ return [
186
+ "",
187
+ "## Glasstrace MCP \u2014 Runtime Debugging Evidence",
188
+ "",
189
+ "Glasstrace is connected via MCP (`glasstrace`). It captures runtime traces from this app and exposes them through tools that disambiguate bugs source alone cannot resolve.",
190
+ "",
191
+ "### Call Glasstrace FIRST when:",
192
+ "- A value passed at runtime is suspect: wrong role, locale, timezone, template, tenant id, or any enum/config that branches downstream.",
193
+ "- The bug is in a side-effect operation: email sent, calendar event created, webhook dispatched, queued job, `after()` callback.",
194
+ "- The user reports a symptom but you do not yet know which code path actually executed.",
195
+ "- You need to confirm a route or procedure ran in the relevant time window before doing a source dive.",
196
+ "",
197
+ "### SKIP Glasstrace when:",
198
+ "- The bug is statically obvious from source (N+1 query, missing await, redundant query, type error, missing null check).",
199
+ "- The change is a refactor whose correctness does not depend on runtime behavior.",
200
+ "- You already have a precise traceId from another source.",
201
+ "",
202
+ "### Workflow",
203
+ "1. Start with `find_trace_candidates`. Pass whatever route or procedure name is natural \u2014 the server normalizes vocabulary and, on miss, returns close matches and a sample of routes actually present in the window.",
204
+ "2. Take the highest-confidence candidate's `suggestedFollowups` and pass them straight to `get_trace` or `get_root_cause`.",
205
+ "3. For side-effect bugs, read `sideEffectSummary` in the `get_trace` / `get_root_cause` response. The allowlisted fields (`templateKey`, `providerOperation`, `role`, `locale`, `timezone`, `status`, `phase`) are the ones that disambiguate payload bugs.",
206
+ "4. If a tool returns empty, READ the response's empty-result envelope before pivoting to source \u2014 each field disambiguates a different reason for the empty result:",
207
+ " - `closeMatches` / `recentRoutesSample` \u2014 your filter vocabulary doesn't match server-side names; the server returns the closest known names + a sample of routes actually present.",
208
+ ' - `windowActivity` \u2014 load-bearing four-way distinguisher. `totalTracesInWindow === 0` AND `totalTracesInTenantEver > 0` means "your time window missed the activity"; `totalTracesInTenantEver === 0` means "this tenant has never produced traces" (SDK not registered, or never hit); `captureConfigBlocksRequest === true` means "the SDK\'s capture config dropped this route"; otherwise the empty result is a vocabulary miss \u2014 see `closeMatches`.',
209
+ " - `humanReadable` \u2014 prose guidance written for the agent.",
210
+ " - `recoveryActions` \u2014 concrete next-call shapes.",
211
+ " - `diagnosticValue` / `recommendedNextStep` \u2014 whether to keep searching or stop.",
212
+ " Empty results carry `notAbsenceProof: true` \u2014 they are never proof the bug did not occur.",
213
+ "",
214
+ "### Tools",
215
+ "- `find_trace_candidates` \u2014 discovery, vocabulary-tolerant filter",
216
+ "- `get_trace` \u2014 exact trace by `traceId`",
217
+ "- `get_root_cause` \u2014 root-cause analysis for a `traceId`",
218
+ "- `get_session_timeline` \u2014 events for a session",
219
+ "- `get_latest_error` / `get_error_list` \u2014 recent server errors",
220
+ "",
221
+ "Side-effect evidence is allowlisted and compact by design. Fields you don't see may have been omitted by policy, not absent at runtime.",
222
+ ""
223
+ ].join("\n");
224
+ }
225
+
226
+ // src/agent-detection/configs.ts
227
+ function generateMcpConfig(agent, endpoint, bearer) {
228
+ if (!endpoint || endpoint.trim() === "") {
229
+ throw new Error("endpoint must not be empty");
230
+ }
231
+ if (!bearer || bearer.trim() === "") {
232
+ throw new Error("bearer must not be empty");
233
+ }
234
+ switch (agent.name) {
235
+ case "claude":
236
+ return JSON.stringify(
237
+ {
238
+ mcpServers: {
239
+ glasstrace: {
240
+ type: "http",
241
+ url: endpoint,
242
+ headers: {
243
+ Authorization: `Bearer ${bearer}`
244
+ }
245
+ }
246
+ }
247
+ },
248
+ null,
249
+ 2
250
+ );
251
+ case "codex": {
252
+ const safeEndpoint = endpoint.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
253
+ return [
254
+ "[mcp_servers.glasstrace]",
255
+ `url = "${safeEndpoint}"`,
256
+ `bearer_token_env_var = "GLASSTRACE_API_KEY"`,
257
+ ""
258
+ ].join("\n");
259
+ }
260
+ case "gemini":
261
+ return JSON.stringify(
262
+ {
263
+ mcpServers: {
264
+ glasstrace: {
265
+ httpUrl: endpoint,
266
+ headers: {
267
+ Authorization: `Bearer ${bearer}`
268
+ }
269
+ }
270
+ }
271
+ },
272
+ null,
273
+ 2
274
+ );
275
+ case "cursor":
276
+ return JSON.stringify(
277
+ {
278
+ mcpServers: {
279
+ glasstrace: {
280
+ type: "http",
281
+ url: endpoint,
282
+ headers: {
283
+ Authorization: `Bearer ${bearer}`
284
+ }
285
+ }
286
+ }
287
+ },
288
+ null,
289
+ 2
290
+ );
291
+ case "windsurf":
292
+ return JSON.stringify(
293
+ {
294
+ mcpServers: {
295
+ glasstrace: {
296
+ type: "http",
297
+ url: endpoint,
298
+ headers: {
299
+ Authorization: `Bearer ${bearer}`
300
+ }
301
+ }
302
+ }
303
+ },
304
+ null,
305
+ 2
306
+ );
307
+ case "generic":
308
+ return JSON.stringify(
309
+ {
310
+ mcpServers: {
311
+ glasstrace: {
312
+ type: "http",
313
+ url: endpoint,
314
+ headers: {
315
+ Authorization: `Bearer ${bearer}`
316
+ }
317
+ }
318
+ }
319
+ },
320
+ null,
321
+ 2
322
+ );
323
+ default: {
324
+ const _exhaustive = agent.name;
325
+ throw new Error(`Unknown agent: ${_exhaustive}`);
326
+ }
327
+ }
328
+ }
329
+ var SDK_VERSION_STAMP_PATTERN = /^[A-Za-z0-9.+-]+$/;
330
+ function htmlMarkers(sdkVersion) {
331
+ return {
332
+ start: `<!-- glasstrace:mcp:start v=${sdkVersion} -->`,
333
+ end: "<!-- glasstrace:mcp:end -->"
334
+ };
335
+ }
336
+ function hashMarkers(sdkVersion) {
337
+ return {
338
+ start: `# glasstrace:mcp:start v=${sdkVersion}`,
339
+ end: "# glasstrace:mcp:end"
340
+ };
341
+ }
342
+ function generateInfoSection(agent, endpoint, sdkVersion) {
343
+ if (!endpoint || endpoint.trim() === "") {
344
+ throw new Error("endpoint must not be empty");
345
+ }
346
+ if (!sdkVersion || sdkVersion.trim() === "") {
347
+ throw new Error("sdkVersion must not be empty");
348
+ }
349
+ if (!SDK_VERSION_STAMP_PATTERN.test(sdkVersion)) {
350
+ throw new Error(
351
+ "sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
352
+ );
353
+ }
354
+ const content = buildAgentInstructionBody();
355
+ switch (agent.name) {
356
+ case "claude":
357
+ case "codex":
358
+ case "gemini":
359
+ case "windsurf":
360
+ case "generic": {
361
+ const m = htmlMarkers(sdkVersion);
362
+ return `${m.start}
363
+ ${content}${m.end}
364
+ `;
365
+ }
366
+ case "cursor": {
367
+ const m = htmlMarkers(sdkVersion);
368
+ return `${m.start}
369
+ ${content}${m.end}
370
+ `;
371
+ }
372
+ default: {
373
+ const _exhaustive = agent.name;
374
+ throw new Error(`Unknown agent: ${_exhaustive}`);
375
+ }
376
+ }
377
+ }
378
+ function generateInfoSectionForCursorrulesLegacy(endpoint, sdkVersion) {
379
+ if (!endpoint || endpoint.trim() === "") {
380
+ throw new Error("endpoint must not be empty");
381
+ }
382
+ if (!sdkVersion || sdkVersion.trim() === "") {
383
+ throw new Error("sdkVersion must not be empty");
384
+ }
385
+ if (!SDK_VERSION_STAMP_PATTERN.test(sdkVersion)) {
386
+ throw new Error(
387
+ "sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
388
+ );
389
+ }
390
+ const content = buildAgentInstructionBody();
391
+ const m = hashMarkers(sdkVersion);
392
+ return `${m.start}
393
+ ${content}${m.end}
394
+ `;
395
+ }
396
+ function generateInfoSectionForCursorMdc(endpoint, sdkVersion) {
397
+ if (!endpoint || endpoint.trim() === "") {
398
+ throw new Error("endpoint must not be empty");
399
+ }
400
+ if (!sdkVersion || sdkVersion.trim() === "") {
401
+ throw new Error("sdkVersion must not be empty");
402
+ }
403
+ if (!SDK_VERSION_STAMP_PATTERN.test(sdkVersion)) {
404
+ throw new Error(
405
+ "sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
406
+ );
407
+ }
408
+ const content = buildAgentInstructionBody();
409
+ const m = htmlMarkers(sdkVersion);
410
+ return [
411
+ "---",
412
+ "description: Glasstrace MCP runtime debugging tools \u2014 runtime evidence the agent reads when source alone cannot resolve a bug",
413
+ "alwaysApply: true",
414
+ "---",
415
+ "",
416
+ `${m.start}
417
+ ${content}${m.end}
418
+ `
419
+ ].join("\n");
420
+ }
421
+
422
+ // src/agent-detection/inject-all-targets.ts
423
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
424
+ import { dirname as dirname2, join as join2 } from "node:path";
425
+ async function injectAllTargets(agents, endpoint, sdkVersion, projectRoot) {
426
+ const writtenAgentsMd = /* @__PURE__ */ new Set();
427
+ for (const agent of agents) {
428
+ const targets = computeTargets(agent, projectRoot);
429
+ for (const target of targets) {
430
+ if (target.isAgentsMdCompanion) {
431
+ if (writtenAgentsMd.has(target.path)) {
432
+ continue;
433
+ }
434
+ writtenAgentsMd.add(target.path);
435
+ }
436
+ let createContent;
437
+ let managedSectionOnly;
438
+ if (target.kind === "cursor-mdc") {
439
+ createContent = generateInfoSectionForCursorMdc(endpoint, sdkVersion);
440
+ managedSectionOnly = generateInfoSection(agent, endpoint, sdkVersion);
441
+ } else if (target.kind === "cursorrules-legacy") {
442
+ createContent = generateInfoSectionForCursorrulesLegacy(
443
+ endpoint,
444
+ sdkVersion
445
+ );
446
+ managedSectionOnly = createContent;
447
+ } else {
448
+ createContent = generateInfoSection(agent, endpoint, sdkVersion);
449
+ managedSectionOnly = createContent;
450
+ }
451
+ if (managedSectionOnly === "") continue;
452
+ await writeManagedSectionToTarget(
453
+ target.path,
454
+ createContent,
455
+ managedSectionOnly
456
+ );
457
+ }
458
+ }
459
+ }
460
+ function foundDirFromAgent(agent) {
461
+ if (agent.infoFilePath === null) return null;
462
+ switch (agent.name) {
463
+ case "claude":
464
+ case "codex":
465
+ case "gemini":
466
+ case "generic":
467
+ return dirname2(agent.infoFilePath);
468
+ case "cursor":
469
+ return dirname2(dirname2(dirname2(agent.infoFilePath)));
470
+ case "windsurf":
471
+ return dirname2(dirname2(dirname2(agent.infoFilePath)));
472
+ }
473
+ }
474
+ function computeTargets(agent, projectRoot) {
475
+ const targets = [];
476
+ const foundDir = foundDirFromAgent(agent) ?? projectRoot;
477
+ switch (agent.name) {
478
+ case "claude": {
479
+ if (agent.infoFilePath) {
480
+ targets.push({
481
+ path: agent.infoFilePath,
482
+ kind: "primary",
483
+ isAgentsMdCompanion: false
484
+ });
485
+ }
486
+ targets.push({
487
+ path: join2(foundDir, "AGENTS.md"),
488
+ kind: "agents-md-companion",
489
+ isAgentsMdCompanion: true
490
+ });
491
+ return targets;
492
+ }
493
+ case "codex": {
494
+ if (agent.infoFilePath) {
495
+ targets.push({
496
+ path: agent.infoFilePath,
497
+ kind: "primary",
498
+ isAgentsMdCompanion: true
499
+ });
500
+ }
501
+ return targets;
502
+ }
503
+ case "gemini": {
504
+ if (agent.infoFilePath) {
505
+ targets.push({
506
+ path: agent.infoFilePath,
507
+ kind: "primary",
508
+ isAgentsMdCompanion: false
509
+ });
510
+ }
511
+ targets.push({
512
+ path: join2(foundDir, "AGENTS.md"),
513
+ kind: "agents-md-companion",
514
+ isAgentsMdCompanion: true
515
+ });
516
+ return targets;
517
+ }
518
+ case "cursor": {
519
+ if (agent.infoFilePath) {
520
+ targets.push({
521
+ path: agent.infoFilePath,
522
+ kind: "cursor-mdc",
523
+ isAgentsMdCompanion: false
524
+ });
525
+ targets.push({
526
+ path: join2(foundDir, ".cursorrules"),
527
+ kind: "cursorrules-legacy",
528
+ isAgentsMdCompanion: false
529
+ });
530
+ }
531
+ targets.push({
532
+ path: join2(foundDir, "AGENTS.md"),
533
+ kind: "agents-md-companion",
534
+ isAgentsMdCompanion: true
535
+ });
536
+ return targets;
537
+ }
538
+ case "windsurf": {
539
+ if (agent.infoFilePath) {
540
+ targets.push({
541
+ path: agent.infoFilePath,
542
+ kind: "primary",
543
+ isAgentsMdCompanion: false
544
+ });
545
+ }
546
+ targets.push({
547
+ path: join2(foundDir, "AGENTS.md"),
548
+ kind: "agents-md-companion",
549
+ isAgentsMdCompanion: true
550
+ });
551
+ return targets;
552
+ }
553
+ case "generic": {
554
+ if (agent.infoFilePath) {
555
+ targets.push({
556
+ path: agent.infoFilePath,
557
+ kind: "primary",
558
+ isAgentsMdCompanion: true
559
+ });
560
+ }
561
+ return targets;
562
+ }
563
+ default: {
564
+ const _exhaustive = agent.name;
565
+ throw new Error(`Unknown agent: ${_exhaustive}`);
566
+ }
567
+ }
568
+ }
569
+ async function writeManagedSectionToTarget(filePath, createContent, managedSectionOnly) {
570
+ let existingContent = null;
571
+ try {
572
+ existingContent = await readFile(filePath, "utf-8");
573
+ } catch (err) {
574
+ const code = err.code;
575
+ if (code !== "ENOENT") {
576
+ emitTargetWarning(filePath, "read", err);
577
+ return;
578
+ }
579
+ }
580
+ if (existingContent === null) {
581
+ try {
582
+ await mkdir(dirname2(filePath), { recursive: true });
583
+ await writeFile(filePath, createContent, "utf-8");
584
+ } catch (err) {
585
+ emitTargetWarning(filePath, "write", err);
586
+ return;
587
+ }
588
+ return;
589
+ }
590
+ const lines = existingContent.split("\n");
591
+ const boundaries = findMarkerBoundaries(lines);
592
+ let newContent;
593
+ if (boundaries !== null) {
594
+ const before = lines.slice(0, boundaries.startIdx);
595
+ const after = lines.slice(boundaries.endIdx + 1);
596
+ const contentWithoutTrailingNewline = managedSectionOnly.endsWith("\n") ? managedSectionOnly.slice(0, -1) : managedSectionOnly;
597
+ newContent = [...before, contentWithoutTrailingNewline, ...after].join(
598
+ "\n"
599
+ );
600
+ } else {
601
+ const separator = existingContent.endsWith("\n") ? "\n" : "\n\n";
602
+ newContent = existingContent + separator + managedSectionOnly;
603
+ }
604
+ try {
605
+ await writeFile(filePath, newContent, "utf-8");
606
+ } catch (err) {
607
+ emitTargetWarning(filePath, "write", err);
608
+ }
609
+ }
610
+ function emitTargetWarning(filePath, op, err) {
611
+ const code = err.code;
612
+ let qualifier;
613
+ switch (code) {
614
+ case "EACCES":
615
+ case "EPERM":
616
+ qualifier = "permission denied";
617
+ break;
618
+ case "EROFS":
619
+ qualifier = "filesystem read-only";
620
+ break;
621
+ case "ENOSPC":
622
+ qualifier = "disk full";
623
+ break;
624
+ case "ENAMETOOLONG":
625
+ qualifier = "path too long";
626
+ break;
627
+ case "ENOTDIR":
628
+ qualifier = "not a directory";
629
+ break;
630
+ case "EISDIR":
631
+ qualifier = "is a directory";
632
+ break;
633
+ default:
634
+ qualifier = "I/O error";
635
+ break;
636
+ }
637
+ try {
638
+ process.stderr.write(
639
+ `Warning: cannot ${op} info file ${filePath}: ${qualifier}
640
+ `
641
+ );
642
+ } catch {
643
+ }
644
+ }
645
+
646
+ export {
647
+ detectAgents,
648
+ generateMcpConfig,
649
+ injectAllTargets
650
+ };
651
+ //# sourceMappingURL=chunk-KOYZJN6G.js.map