@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.
- package/README.md +43 -5
- package/dist/{chunk-WQF7ZQOM.js → chunk-DQFGNX3H.js} +13 -8
- package/dist/{chunk-WQF7ZQOM.js.map → chunk-DQFGNX3H.js.map} +1 -1
- package/dist/{chunk-UMGZJYC4.js → chunk-FQ4SEG6Y.js} +8 -3
- package/dist/chunk-FQ4SEG6Y.js.map +1 -0
- package/dist/chunk-KOYZJN6G.js +651 -0
- package/dist/chunk-KOYZJN6G.js.map +1 -0
- package/dist/{chunk-ZBQQXVHD.js → chunk-YIEXKQYP.js} +2 -67
- package/dist/chunk-YIEXKQYP.js.map +1 -0
- package/dist/cli/init.cjs +460 -127
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +29 -16
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +346 -98
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +32 -14
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs +6 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.js +7 -2
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/uninit.cjs +6 -1
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.js +2 -2
- package/dist/cli/upgrade-instructions.cjs +390 -113
- package/dist/cli/upgrade-instructions.cjs.map +1 -1
- package/dist/cli/upgrade-instructions.js +70 -18
- package/dist/cli/upgrade-instructions.js.map +1 -1
- package/dist/index.cjs +11 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/node-entry.cjs +11 -6
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-TJ46YOGJ.js +0 -355
- package/dist/chunk-TJ46YOGJ.js.map +0 -1
- package/dist/chunk-UMGZJYC4.js.map +0 -1
- package/dist/chunk-ZBQQXVHD.js.map +0 -1
package/dist/cli/uninit.js
CHANGED
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
unwrapCJSExport,
|
|
13
13
|
unwrapExport,
|
|
14
14
|
writeShutdownMarker
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-FQ4SEG6Y.js";
|
|
16
16
|
import "../chunk-RL43PU2X.js";
|
|
17
17
|
import "../chunk-6RKS3DNA.js";
|
|
18
18
|
import "../chunk-NB7GJE4S.js";
|
|
19
|
-
import "../chunk-
|
|
19
|
+
import "../chunk-YIEXKQYP.js";
|
|
20
20
|
import "../chunk-NSBPE2FW.js";
|
|
21
21
|
export {
|
|
22
22
|
findMatchingDelimiter,
|
|
@@ -46,33 +46,61 @@ var AGENT_RULES = [
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
name: "codex",
|
|
49
|
+
// Codex 2026 default discovery is `AGENTS.override.md` → `AGENTS.md` →
|
|
50
|
+
// opt-in `project_doc_fallback_filenames`; `codex.md` is NOT in the
|
|
51
|
+
// default fallback list. Detection requires Codex-specific markers
|
|
52
|
+
// (`codex.md` legacy, `.codex/` config dir) — `AGENTS.md` is NOT
|
|
53
|
+
// included as a marker because the SDK now writes `AGENTS.md`
|
|
54
|
+
// broadly via the multi-target dispatcher's companion writes; if
|
|
55
|
+
// `AGENTS.md` were a Codex marker, every project with the SDK's
|
|
56
|
+
// own companion AGENTS.md would re-classify as Codex on subsequent
|
|
57
|
+
// detect runs and trigger unintended `.codex/config.toml` writes
|
|
58
|
+
// (Codex P1 + Copilot P1 review of Wave 18 PR #274). The canonical
|
|
59
|
+
// write destination remains AGENTS.md regardless of which marker
|
|
60
|
+
// classified the project.
|
|
49
61
|
markers: ["codex.md", ".codex"],
|
|
50
62
|
mcpConfigPath: (dir) => (0, import_node_path.join)(dir, ".codex", "config.toml"),
|
|
51
|
-
infoFilePath: (dir) => (0, import_node_path.join)(dir, "
|
|
63
|
+
infoFilePath: (dir) => (0, import_node_path.join)(dir, "AGENTS.md"),
|
|
52
64
|
cliBinary: "codex",
|
|
53
65
|
registrationCommand: "npx glasstrace mcp add --agent codex"
|
|
54
66
|
},
|
|
55
67
|
{
|
|
56
68
|
name: "gemini",
|
|
57
|
-
markers: [".gemini"],
|
|
69
|
+
markers: [".gemini", "GEMINI.md"],
|
|
58
70
|
mcpConfigPath: (dir) => (0, import_node_path.join)(dir, ".gemini", "settings.json"),
|
|
59
|
-
infoFilePath: () =>
|
|
71
|
+
infoFilePath: (dir) => (0, import_node_path.join)(dir, "GEMINI.md"),
|
|
60
72
|
cliBinary: "gemini",
|
|
61
73
|
registrationCommand: "npx glasstrace mcp add --agent gemini"
|
|
62
74
|
},
|
|
63
75
|
{
|
|
64
76
|
name: "cursor",
|
|
77
|
+
// `.cursor/rules/*.mdc` is the current canonical format per Cursor's
|
|
78
|
+
// 2026 docs. `.cursorrules` (single file) is supported-but-deprecated
|
|
79
|
+
// and stays as a transitional fallback that the multi-target write
|
|
80
|
+
// helper writes unconditionally alongside the .mdc canonical.
|
|
65
81
|
markers: [".cursor", ".cursorrules"],
|
|
66
82
|
mcpConfigPath: (dir) => (0, import_node_path.join)(dir, ".cursor", "mcp.json"),
|
|
67
|
-
infoFilePath: (dir) => (0, import_node_path.join)(dir, ".
|
|
83
|
+
infoFilePath: (dir) => (0, import_node_path.join)(dir, ".cursor", "rules", "glasstrace.mdc"),
|
|
68
84
|
cliBinary: null,
|
|
69
85
|
registrationCommand: "npx glasstrace mcp add --agent cursor"
|
|
70
86
|
},
|
|
71
87
|
{
|
|
72
88
|
name: "windsurf",
|
|
73
|
-
|
|
89
|
+
// Windsurf's current canonical workspace-rules format is
|
|
90
|
+
// `.windsurf/rules/*.md`. AGENTS.md is a parallel cross-tool
|
|
91
|
+
// mechanism Windsurf also reads BUT is NOT included as a Windsurf
|
|
92
|
+
// detection marker — the SDK writes `AGENTS.md` broadly via the
|
|
93
|
+
// multi-target dispatcher's companion writes, so treating
|
|
94
|
+
// `AGENTS.md` as a Windsurf marker would re-classify every
|
|
95
|
+
// SDK-managed project as Windsurf and cause `glasstrace uninit`
|
|
96
|
+
// to mutate the global `~/.codeium/windsurf/mcp_config.json` for
|
|
97
|
+
// non-Windsurf projects (Codex P1 + Copilot P1 review of Wave 18
|
|
98
|
+
// PR #274). The single-file `.windsurfrules` is the deprecated
|
|
99
|
+
// legacy form — recognized as a marker so legacy projects classify
|
|
100
|
+
// correctly, but the SDK no longer writes to it.
|
|
101
|
+
markers: [".windsurf", ".windsurfrules"],
|
|
74
102
|
mcpConfigPath: () => (0, import_node_path.join)((0, import_node_os.homedir)(), ".codeium", "windsurf", "mcp_config.json"),
|
|
75
|
-
infoFilePath: (dir) => (0, import_node_path.join)(dir, ".
|
|
103
|
+
infoFilePath: (dir) => (0, import_node_path.join)(dir, ".windsurf", "rules", "glasstrace.md"),
|
|
76
104
|
cliBinary: null,
|
|
77
105
|
registrationCommand: "npx glasstrace mcp add --agent windsurf"
|
|
78
106
|
}
|
|
@@ -159,10 +187,7 @@ async function detectAgents(projectRoot) {
|
|
|
159
187
|
continue;
|
|
160
188
|
}
|
|
161
189
|
seenAgents.add(rule.name);
|
|
162
|
-
|
|
163
|
-
if (infoFilePath !== null && !await pathExists(infoFilePath)) {
|
|
164
|
-
infoFilePath = null;
|
|
165
|
-
}
|
|
190
|
+
const infoFilePath = rule.infoFilePath(foundDir);
|
|
166
191
|
const cliAvailable = rule.cliBinary ? await isCliAvailable(rule.cliBinary) : false;
|
|
167
192
|
detected.push({
|
|
168
193
|
name: rule.name,
|
|
@@ -175,13 +200,62 @@ async function detectAgents(projectRoot) {
|
|
|
175
200
|
detected.push({
|
|
176
201
|
name: "generic",
|
|
177
202
|
mcpConfigPath: (0, import_node_path.join)(resolvedRoot, ".glasstrace", "mcp.json"),
|
|
178
|
-
infoFilePath:
|
|
203
|
+
infoFilePath: (0, import_node_path.join)(resolvedRoot, "AGENTS.md"),
|
|
179
204
|
cliAvailable: false,
|
|
180
205
|
registrationCommand: null
|
|
181
206
|
});
|
|
182
207
|
return detected;
|
|
183
208
|
}
|
|
184
209
|
|
|
210
|
+
// src/agent-detection/inject.ts
|
|
211
|
+
var import_promises2 = require("node:fs/promises");
|
|
212
|
+
var HTML_START_RE = /^<!--\s*glasstrace:mcp:start(?:\s+v=([^\s>]+))?\s*-->$/;
|
|
213
|
+
var HTML_END = "<!-- glasstrace:mcp:end -->";
|
|
214
|
+
var HASH_START_RE = /^#\s*glasstrace:mcp:start(?:\s+v=(\S+))?$/;
|
|
215
|
+
var HASH_END = "# glasstrace:mcp:end";
|
|
216
|
+
function parseStartMarkerLine(line) {
|
|
217
|
+
const trimmed = line.trim();
|
|
218
|
+
const html = HTML_START_RE.exec(trimmed);
|
|
219
|
+
if (html !== null) {
|
|
220
|
+
return { kind: "html", stamp: html[1] ?? null };
|
|
221
|
+
}
|
|
222
|
+
const hash = HASH_START_RE.exec(trimmed);
|
|
223
|
+
if (hash !== null) {
|
|
224
|
+
return { kind: "hash", stamp: hash[1] ?? null };
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
function isEndMarker(line) {
|
|
229
|
+
const trimmed = line.trim();
|
|
230
|
+
return trimmed === HTML_END || trimmed === HASH_END;
|
|
231
|
+
}
|
|
232
|
+
function findMarkerBoundaries(lines) {
|
|
233
|
+
let startIdx = -1;
|
|
234
|
+
for (let i = 0; i < lines.length; i++) {
|
|
235
|
+
if (parseStartMarkerLine(lines[i]) !== null) {
|
|
236
|
+
startIdx = i;
|
|
237
|
+
} else if (startIdx !== -1 && isEndMarker(lines[i])) {
|
|
238
|
+
return { startIdx, endIdx: i };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
async function hasManagedSection(filePath) {
|
|
244
|
+
let content;
|
|
245
|
+
try {
|
|
246
|
+
content = await (0, import_promises2.readFile)(filePath, "utf-8");
|
|
247
|
+
} catch (err) {
|
|
248
|
+
const code = err.code;
|
|
249
|
+
if (code === "ENOENT") return false;
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
return findMarkerBoundaries(content.split("\n")) !== null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/agent-detection/inject-all-targets.ts
|
|
256
|
+
var import_promises3 = require("node:fs/promises");
|
|
257
|
+
var import_node_path2 = require("node:path");
|
|
258
|
+
|
|
185
259
|
// src/agent-detection/agent-instruction-text.ts
|
|
186
260
|
function buildAgentInstructionBody() {
|
|
187
261
|
return [
|
|
@@ -205,7 +279,13 @@ function buildAgentInstructionBody() {
|
|
|
205
279
|
"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.",
|
|
206
280
|
"2. Take the highest-confidence candidate's `suggestedFollowups` and pass them straight to `get_trace` or `get_root_cause`.",
|
|
207
281
|
"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.",
|
|
208
|
-
"4. If a tool returns empty, READ the response's
|
|
282
|
+
"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:",
|
|
283
|
+
" - `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.",
|
|
284
|
+
' - `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`.',
|
|
285
|
+
" - `humanReadable` \u2014 prose guidance written for the agent.",
|
|
286
|
+
" - `recoveryActions` \u2014 concrete next-call shapes.",
|
|
287
|
+
" - `diagnosticValue` / `recommendedNextStep` \u2014 whether to keep searching or stop.",
|
|
288
|
+
" Empty results carry `notAbsenceProof: true` \u2014 they are never proof the bug did not occur.",
|
|
209
289
|
"",
|
|
210
290
|
"### Tools",
|
|
211
291
|
"- `find_trace_candidates` \u2014 discovery, vocabulary-tolerant filter",
|
|
@@ -247,110 +327,235 @@ function generateInfoSection(agent, endpoint, sdkVersion) {
|
|
|
247
327
|
}
|
|
248
328
|
const content = buildAgentInstructionBody();
|
|
249
329
|
switch (agent.name) {
|
|
250
|
-
case "claude":
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
case "codex": {
|
|
330
|
+
case "claude":
|
|
331
|
+
case "codex":
|
|
332
|
+
case "gemini":
|
|
333
|
+
case "windsurf":
|
|
334
|
+
case "generic": {
|
|
257
335
|
const m = htmlMarkers(sdkVersion);
|
|
258
336
|
return `${m.start}
|
|
259
337
|
${content}${m.end}
|
|
260
338
|
`;
|
|
261
339
|
}
|
|
262
340
|
case "cursor": {
|
|
263
|
-
const m =
|
|
341
|
+
const m = htmlMarkers(sdkVersion);
|
|
264
342
|
return `${m.start}
|
|
265
343
|
${content}${m.end}
|
|
266
344
|
`;
|
|
267
345
|
}
|
|
268
|
-
case "gemini":
|
|
269
|
-
case "windsurf":
|
|
270
|
-
case "generic":
|
|
271
|
-
return "";
|
|
272
346
|
default: {
|
|
273
347
|
const _exhaustive = agent.name;
|
|
274
348
|
throw new Error(`Unknown agent: ${_exhaustive}`);
|
|
275
349
|
}
|
|
276
350
|
}
|
|
277
351
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
var import_node_path2 = require("node:path");
|
|
282
|
-
var HTML_START_RE = /^<!--\s*glasstrace:mcp:start(?:\s+v=([^\s>]+))?\s*-->$/;
|
|
283
|
-
var HTML_END = "<!-- glasstrace:mcp:end -->";
|
|
284
|
-
var HASH_START_RE = /^#\s*glasstrace:mcp:start(?:\s+v=(\S+))?$/;
|
|
285
|
-
var HASH_END = "# glasstrace:mcp:end";
|
|
286
|
-
function parseStartMarkerLine(line) {
|
|
287
|
-
const trimmed = line.trim();
|
|
288
|
-
const html = HTML_START_RE.exec(trimmed);
|
|
289
|
-
if (html !== null) {
|
|
290
|
-
return { kind: "html", stamp: html[1] ?? null };
|
|
352
|
+
function generateInfoSectionForCursorrulesLegacy(endpoint, sdkVersion) {
|
|
353
|
+
if (!endpoint || endpoint.trim() === "") {
|
|
354
|
+
throw new Error("endpoint must not be empty");
|
|
291
355
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return { kind: "hash", stamp: hash[1] ?? null };
|
|
356
|
+
if (!sdkVersion || sdkVersion.trim() === "") {
|
|
357
|
+
throw new Error("sdkVersion must not be empty");
|
|
295
358
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
359
|
+
if (!SDK_VERSION_STAMP_PATTERN.test(sdkVersion)) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
"sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
const content = buildAgentInstructionBody();
|
|
365
|
+
const m = hashMarkers(sdkVersion);
|
|
366
|
+
return `${m.start}
|
|
367
|
+
${content}${m.end}
|
|
368
|
+
`;
|
|
301
369
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
370
|
+
function generateInfoSectionForCursorMdc(endpoint, sdkVersion) {
|
|
371
|
+
if (!endpoint || endpoint.trim() === "") {
|
|
372
|
+
throw new Error("endpoint must not be empty");
|
|
373
|
+
}
|
|
374
|
+
if (!sdkVersion || sdkVersion.trim() === "") {
|
|
375
|
+
throw new Error("sdkVersion must not be empty");
|
|
376
|
+
}
|
|
377
|
+
if (!SDK_VERSION_STAMP_PATTERN.test(sdkVersion)) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const content = buildAgentInstructionBody();
|
|
383
|
+
const m = htmlMarkers(sdkVersion);
|
|
384
|
+
return [
|
|
385
|
+
"---",
|
|
386
|
+
"description: Glasstrace MCP runtime debugging tools \u2014 runtime evidence the agent reads when source alone cannot resolve a bug",
|
|
387
|
+
"alwaysApply: true",
|
|
388
|
+
"---",
|
|
389
|
+
"",
|
|
390
|
+
`${m.start}
|
|
391
|
+
${content}${m.end}
|
|
392
|
+
`
|
|
393
|
+
].join("\n");
|
|
305
394
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
395
|
+
|
|
396
|
+
// src/agent-detection/inject-all-targets.ts
|
|
397
|
+
async function injectAllTargets(agents, endpoint, sdkVersion, projectRoot) {
|
|
398
|
+
const writtenAgentsMd = /* @__PURE__ */ new Set();
|
|
399
|
+
for (const agent of agents) {
|
|
400
|
+
const targets = computeTargets(agent, projectRoot);
|
|
401
|
+
for (const target of targets) {
|
|
402
|
+
if (target.isAgentsMdCompanion) {
|
|
403
|
+
if (writtenAgentsMd.has(target.path)) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
writtenAgentsMd.add(target.path);
|
|
407
|
+
}
|
|
408
|
+
let createContent;
|
|
409
|
+
let managedSectionOnly;
|
|
410
|
+
if (target.kind === "cursor-mdc") {
|
|
411
|
+
createContent = generateInfoSectionForCursorMdc(endpoint, sdkVersion);
|
|
412
|
+
managedSectionOnly = generateInfoSection(agent, endpoint, sdkVersion);
|
|
413
|
+
} else if (target.kind === "cursorrules-legacy") {
|
|
414
|
+
createContent = generateInfoSectionForCursorrulesLegacy(
|
|
415
|
+
endpoint,
|
|
416
|
+
sdkVersion
|
|
417
|
+
);
|
|
418
|
+
managedSectionOnly = createContent;
|
|
419
|
+
} else {
|
|
420
|
+
createContent = generateInfoSection(agent, endpoint, sdkVersion);
|
|
421
|
+
managedSectionOnly = createContent;
|
|
422
|
+
}
|
|
423
|
+
if (managedSectionOnly === "") continue;
|
|
424
|
+
await writeManagedSectionToTarget(
|
|
425
|
+
target.path,
|
|
426
|
+
createContent,
|
|
427
|
+
managedSectionOnly
|
|
428
|
+
);
|
|
313
429
|
}
|
|
314
430
|
}
|
|
315
|
-
return null;
|
|
316
431
|
}
|
|
317
|
-
|
|
318
|
-
if (agent.infoFilePath === null)
|
|
319
|
-
|
|
432
|
+
function foundDirFromAgent(agent) {
|
|
433
|
+
if (agent.infoFilePath === null) return null;
|
|
434
|
+
switch (agent.name) {
|
|
435
|
+
case "claude":
|
|
436
|
+
case "codex":
|
|
437
|
+
case "gemini":
|
|
438
|
+
case "generic":
|
|
439
|
+
return (0, import_node_path2.dirname)(agent.infoFilePath);
|
|
440
|
+
case "cursor":
|
|
441
|
+
return (0, import_node_path2.dirname)((0, import_node_path2.dirname)((0, import_node_path2.dirname)(agent.infoFilePath)));
|
|
442
|
+
case "windsurf":
|
|
443
|
+
return (0, import_node_path2.dirname)((0, import_node_path2.dirname)((0, import_node_path2.dirname)(agent.infoFilePath)));
|
|
320
444
|
}
|
|
321
|
-
|
|
322
|
-
|
|
445
|
+
}
|
|
446
|
+
function computeTargets(agent, projectRoot) {
|
|
447
|
+
const targets = [];
|
|
448
|
+
const foundDir = foundDirFromAgent(agent) ?? projectRoot;
|
|
449
|
+
switch (agent.name) {
|
|
450
|
+
case "claude": {
|
|
451
|
+
if (agent.infoFilePath) {
|
|
452
|
+
targets.push({
|
|
453
|
+
path: agent.infoFilePath,
|
|
454
|
+
kind: "primary",
|
|
455
|
+
isAgentsMdCompanion: false
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
targets.push({
|
|
459
|
+
path: (0, import_node_path2.join)(foundDir, "AGENTS.md"),
|
|
460
|
+
kind: "agents-md-companion",
|
|
461
|
+
isAgentsMdCompanion: true
|
|
462
|
+
});
|
|
463
|
+
return targets;
|
|
464
|
+
}
|
|
465
|
+
case "codex": {
|
|
466
|
+
if (agent.infoFilePath) {
|
|
467
|
+
targets.push({
|
|
468
|
+
path: agent.infoFilePath,
|
|
469
|
+
kind: "primary",
|
|
470
|
+
isAgentsMdCompanion: true
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
return targets;
|
|
474
|
+
}
|
|
475
|
+
case "gemini": {
|
|
476
|
+
if (agent.infoFilePath) {
|
|
477
|
+
targets.push({
|
|
478
|
+
path: agent.infoFilePath,
|
|
479
|
+
kind: "primary",
|
|
480
|
+
isAgentsMdCompanion: false
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
targets.push({
|
|
484
|
+
path: (0, import_node_path2.join)(foundDir, "AGENTS.md"),
|
|
485
|
+
kind: "agents-md-companion",
|
|
486
|
+
isAgentsMdCompanion: true
|
|
487
|
+
});
|
|
488
|
+
return targets;
|
|
489
|
+
}
|
|
490
|
+
case "cursor": {
|
|
491
|
+
if (agent.infoFilePath) {
|
|
492
|
+
targets.push({
|
|
493
|
+
path: agent.infoFilePath,
|
|
494
|
+
kind: "cursor-mdc",
|
|
495
|
+
isAgentsMdCompanion: false
|
|
496
|
+
});
|
|
497
|
+
targets.push({
|
|
498
|
+
path: (0, import_node_path2.join)(foundDir, ".cursorrules"),
|
|
499
|
+
kind: "cursorrules-legacy",
|
|
500
|
+
isAgentsMdCompanion: false
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
targets.push({
|
|
504
|
+
path: (0, import_node_path2.join)(foundDir, "AGENTS.md"),
|
|
505
|
+
kind: "agents-md-companion",
|
|
506
|
+
isAgentsMdCompanion: true
|
|
507
|
+
});
|
|
508
|
+
return targets;
|
|
509
|
+
}
|
|
510
|
+
case "windsurf": {
|
|
511
|
+
if (agent.infoFilePath) {
|
|
512
|
+
targets.push({
|
|
513
|
+
path: agent.infoFilePath,
|
|
514
|
+
kind: "primary",
|
|
515
|
+
isAgentsMdCompanion: false
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
targets.push({
|
|
519
|
+
path: (0, import_node_path2.join)(foundDir, "AGENTS.md"),
|
|
520
|
+
kind: "agents-md-companion",
|
|
521
|
+
isAgentsMdCompanion: true
|
|
522
|
+
});
|
|
523
|
+
return targets;
|
|
524
|
+
}
|
|
525
|
+
case "generic": {
|
|
526
|
+
if (agent.infoFilePath) {
|
|
527
|
+
targets.push({
|
|
528
|
+
path: agent.infoFilePath,
|
|
529
|
+
kind: "primary",
|
|
530
|
+
isAgentsMdCompanion: true
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return targets;
|
|
534
|
+
}
|
|
535
|
+
default: {
|
|
536
|
+
const _exhaustive = agent.name;
|
|
537
|
+
throw new Error(`Unknown agent: ${_exhaustive}`);
|
|
538
|
+
}
|
|
323
539
|
}
|
|
324
|
-
|
|
540
|
+
}
|
|
541
|
+
async function writeManagedSectionToTarget(filePath, createContent, managedSectionOnly) {
|
|
325
542
|
let existingContent = null;
|
|
326
543
|
try {
|
|
327
|
-
existingContent = await (0,
|
|
544
|
+
existingContent = await (0, import_promises3.readFile)(filePath, "utf-8");
|
|
328
545
|
} catch (err) {
|
|
329
546
|
const code = err.code;
|
|
330
547
|
if (code !== "ENOENT") {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
`Warning: cannot read info file ${filePath}: permission denied
|
|
334
|
-
`
|
|
335
|
-
);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
throw err;
|
|
548
|
+
emitTargetWarning(filePath, "read", err);
|
|
549
|
+
return;
|
|
339
550
|
}
|
|
340
551
|
}
|
|
341
552
|
if (existingContent === null) {
|
|
342
553
|
try {
|
|
343
|
-
await (0,
|
|
344
|
-
await (0,
|
|
554
|
+
await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(filePath), { recursive: true });
|
|
555
|
+
await (0, import_promises3.writeFile)(filePath, createContent, "utf-8");
|
|
345
556
|
} catch (err) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
`Warning: cannot write info file ${filePath}: permission denied
|
|
349
|
-
`
|
|
350
|
-
);
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
throw err;
|
|
557
|
+
emitTargetWarning(filePath, "write", err);
|
|
558
|
+
return;
|
|
354
559
|
}
|
|
355
560
|
return;
|
|
356
561
|
}
|
|
@@ -360,38 +565,89 @@ async function injectInfoSection(agent, content, projectRoot) {
|
|
|
360
565
|
if (boundaries !== null) {
|
|
361
566
|
const before = lines.slice(0, boundaries.startIdx);
|
|
362
567
|
const after = lines.slice(boundaries.endIdx + 1);
|
|
363
|
-
const contentWithoutTrailingNewline =
|
|
364
|
-
newContent = [...before, contentWithoutTrailingNewline, ...after].join(
|
|
568
|
+
const contentWithoutTrailingNewline = managedSectionOnly.endsWith("\n") ? managedSectionOnly.slice(0, -1) : managedSectionOnly;
|
|
569
|
+
newContent = [...before, contentWithoutTrailingNewline, ...after].join(
|
|
570
|
+
"\n"
|
|
571
|
+
);
|
|
365
572
|
} else {
|
|
366
573
|
const separator = existingContent.endsWith("\n") ? "\n" : "\n\n";
|
|
367
|
-
newContent = existingContent + separator +
|
|
574
|
+
newContent = existingContent + separator + managedSectionOnly;
|
|
368
575
|
}
|
|
369
576
|
try {
|
|
370
|
-
await (0,
|
|
577
|
+
await (0, import_promises3.writeFile)(filePath, newContent, "utf-8");
|
|
371
578
|
} catch (err) {
|
|
372
|
-
|
|
373
|
-
process.stderr.write(
|
|
374
|
-
`Warning: cannot write info file ${filePath}: permission denied
|
|
375
|
-
`
|
|
376
|
-
);
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
throw err;
|
|
579
|
+
emitTargetWarning(filePath, "write", err);
|
|
380
580
|
}
|
|
381
581
|
}
|
|
382
|
-
|
|
383
|
-
|
|
582
|
+
function emitTargetWarning(filePath, op, err) {
|
|
583
|
+
const code = err.code;
|
|
584
|
+
let qualifier;
|
|
585
|
+
switch (code) {
|
|
586
|
+
case "EACCES":
|
|
587
|
+
case "EPERM":
|
|
588
|
+
qualifier = "permission denied";
|
|
589
|
+
break;
|
|
590
|
+
case "EROFS":
|
|
591
|
+
qualifier = "filesystem read-only";
|
|
592
|
+
break;
|
|
593
|
+
case "ENOSPC":
|
|
594
|
+
qualifier = "disk full";
|
|
595
|
+
break;
|
|
596
|
+
case "ENAMETOOLONG":
|
|
597
|
+
qualifier = "path too long";
|
|
598
|
+
break;
|
|
599
|
+
case "ENOTDIR":
|
|
600
|
+
qualifier = "not a directory";
|
|
601
|
+
break;
|
|
602
|
+
case "EISDIR":
|
|
603
|
+
qualifier = "is a directory";
|
|
604
|
+
break;
|
|
605
|
+
default:
|
|
606
|
+
qualifier = "I/O error";
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
384
609
|
try {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
610
|
+
process.stderr.write(
|
|
611
|
+
`Warning: cannot ${op} info file ${filePath}: ${qualifier}
|
|
612
|
+
`
|
|
613
|
+
);
|
|
614
|
+
} catch {
|
|
390
615
|
}
|
|
391
|
-
return findMarkerBoundaries(content.split("\n")) !== null;
|
|
392
616
|
}
|
|
393
617
|
|
|
394
618
|
// src/cli/upgrade-instructions.ts
|
|
619
|
+
function legacyDestinationsForAgent(agent) {
|
|
620
|
+
if (agent.infoFilePath === null) {
|
|
621
|
+
return [];
|
|
622
|
+
}
|
|
623
|
+
switch (agent.name) {
|
|
624
|
+
case "codex":
|
|
625
|
+
return [(0, import_node_path3.join)((0, import_node_path3.dirname)(agent.infoFilePath), "codex.md")];
|
|
626
|
+
case "cursor":
|
|
627
|
+
return [
|
|
628
|
+
(0, import_node_path3.join)((0, import_node_path3.dirname)((0, import_node_path3.dirname)((0, import_node_path3.dirname)(agent.infoFilePath))), ".cursorrules")
|
|
629
|
+
];
|
|
630
|
+
case "windsurf":
|
|
631
|
+
return [
|
|
632
|
+
(0, import_node_path3.join)(
|
|
633
|
+
(0, import_node_path3.dirname)((0, import_node_path3.dirname)((0, import_node_path3.dirname)(agent.infoFilePath))),
|
|
634
|
+
".windsurfrules"
|
|
635
|
+
)
|
|
636
|
+
];
|
|
637
|
+
case "claude":
|
|
638
|
+
case "gemini":
|
|
639
|
+
case "generic":
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function anyHasManagedSection(paths) {
|
|
644
|
+
for (const p of paths) {
|
|
645
|
+
if (await hasManagedSection(p)) {
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
395
651
|
function formatPathForOutput(filePath, projectRoot) {
|
|
396
652
|
const rel = (0, import_node_path3.relative)(projectRoot, filePath);
|
|
397
653
|
if (rel === "" || rel.startsWith("..") || (0, import_node_path3.isAbsolute)(rel)) {
|
|
@@ -413,7 +669,8 @@ async function runUpgradeInstructions(options) {
|
|
|
413
669
|
);
|
|
414
670
|
return { exitCode: 1, refreshed, skipped, warnings, errors };
|
|
415
671
|
}
|
|
416
|
-
const sdkVersion = true ? "1.
|
|
672
|
+
const sdkVersion = true ? "1.11.0" : "0.0.0-dev";
|
|
673
|
+
const optedInAgents = [];
|
|
417
674
|
for (const agent of agents) {
|
|
418
675
|
if (agent.infoFilePath === null) {
|
|
419
676
|
continue;
|
|
@@ -422,34 +679,54 @@ async function runUpgradeInstructions(options) {
|
|
|
422
679
|
agent.infoFilePath,
|
|
423
680
|
options.projectRoot
|
|
424
681
|
);
|
|
425
|
-
|
|
682
|
+
const legacyDestinations = legacyDestinationsForAgent(agent);
|
|
683
|
+
let optedIn;
|
|
426
684
|
try {
|
|
427
|
-
|
|
685
|
+
optedIn = await anyHasManagedSection([
|
|
686
|
+
agent.infoFilePath,
|
|
687
|
+
...legacyDestinations
|
|
688
|
+
]);
|
|
428
689
|
} catch (err) {
|
|
429
690
|
warnings.push(
|
|
430
691
|
`Could not inspect ${displayPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
431
692
|
);
|
|
432
693
|
continue;
|
|
433
694
|
}
|
|
434
|
-
if (!
|
|
695
|
+
if (!optedIn) {
|
|
435
696
|
skipped.push(displayPath);
|
|
436
697
|
continue;
|
|
437
698
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
699
|
+
optedInAgents.push(agent);
|
|
700
|
+
}
|
|
701
|
+
if (optedInAgents.length > 0) {
|
|
442
702
|
try {
|
|
443
|
-
await
|
|
444
|
-
|
|
703
|
+
await injectAllTargets(
|
|
704
|
+
optedInAgents,
|
|
705
|
+
MCP_ENDPOINT,
|
|
706
|
+
sdkVersion,
|
|
707
|
+
options.projectRoot
|
|
708
|
+
);
|
|
709
|
+
for (const a of optedInAgents) {
|
|
710
|
+
if (a.infoFilePath !== null) {
|
|
711
|
+
refreshed.push(formatPathForOutput(a.infoFilePath, options.projectRoot));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
445
714
|
} catch (err) {
|
|
446
715
|
errors.push(
|
|
447
|
-
`Failed to refresh
|
|
716
|
+
`Failed to refresh agent-instruction files: ${err instanceof Error ? err.message : String(err)}`
|
|
448
717
|
);
|
|
449
718
|
}
|
|
450
719
|
}
|
|
720
|
+
const refreshedSet = new Set(refreshed);
|
|
721
|
+
const dedupedSkipped = skipped.filter((p) => !refreshedSet.has(p));
|
|
451
722
|
const exitCode = errors.length === 0 ? 0 : 1;
|
|
452
|
-
return {
|
|
723
|
+
return {
|
|
724
|
+
exitCode,
|
|
725
|
+
refreshed,
|
|
726
|
+
skipped: dedupedSkipped,
|
|
727
|
+
warnings,
|
|
728
|
+
errors
|
|
729
|
+
};
|
|
453
730
|
}
|
|
454
731
|
// Annotate the CommonJS export names for ESM import in node:
|
|
455
732
|
0 && (module.exports = {
|