@agentex/agent 0.0.37 → 0.0.39

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 (83) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +54 -17
  3. package/dist/providers/antigravity/codec.d.ts +4 -0
  4. package/dist/providers/antigravity/codec.d.ts.map +1 -0
  5. package/dist/providers/antigravity/codec.js +27 -0
  6. package/dist/providers/antigravity/codec.js.map +1 -0
  7. package/dist/providers/antigravity/connection.d.ts +100 -0
  8. package/dist/providers/antigravity/connection.d.ts.map +1 -0
  9. package/dist/providers/antigravity/connection.js +267 -0
  10. package/dist/providers/antigravity/connection.js.map +1 -0
  11. package/dist/providers/antigravity/discovery.d.ts +11 -0
  12. package/dist/providers/antigravity/discovery.d.ts.map +1 -0
  13. package/dist/providers/antigravity/discovery.js +92 -0
  14. package/dist/providers/antigravity/discovery.js.map +1 -0
  15. package/dist/providers/antigravity/execute.d.ts +9 -0
  16. package/dist/providers/antigravity/execute.d.ts.map +1 -0
  17. package/dist/providers/antigravity/execute.js +206 -0
  18. package/dist/providers/antigravity/execute.js.map +1 -0
  19. package/dist/providers/antigravity/index.d.ts +12 -0
  20. package/dist/providers/antigravity/index.d.ts.map +1 -0
  21. package/dist/providers/antigravity/index.js +46 -0
  22. package/dist/providers/antigravity/index.js.map +1 -0
  23. package/dist/providers/antigravity/parse.d.ts +114 -0
  24. package/dist/providers/antigravity/parse.d.ts.map +1 -0
  25. package/dist/providers/antigravity/parse.js +369 -0
  26. package/dist/providers/antigravity/parse.js.map +1 -0
  27. package/dist/providers/antigravity/probe.d.ts +3 -0
  28. package/dist/providers/antigravity/probe.d.ts.map +1 -0
  29. package/dist/providers/antigravity/probe.js +84 -0
  30. package/dist/providers/antigravity/probe.js.map +1 -0
  31. package/dist/providers/antigravity/runtime.d.ts +30 -0
  32. package/dist/providers/antigravity/runtime.d.ts.map +1 -0
  33. package/dist/providers/antigravity/runtime.js +69 -0
  34. package/dist/providers/antigravity/runtime.js.map +1 -0
  35. package/dist/providers/antigravity/session.d.ts +7 -0
  36. package/dist/providers/antigravity/session.d.ts.map +1 -0
  37. package/dist/providers/antigravity/session.js +317 -0
  38. package/dist/providers/antigravity/session.js.map +1 -0
  39. package/dist/providers/gemini/index.d.ts.map +1 -1
  40. package/dist/providers/gemini/index.js +6 -0
  41. package/dist/providers/gemini/index.js.map +1 -1
  42. package/dist/registry.d.ts.map +1 -1
  43. package/dist/registry.js +2 -0
  44. package/dist/registry.js.map +1 -1
  45. package/dist/types.d.ts +6 -5
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/types.js.map +1 -1
  48. package/dist/utils/auth.d.ts +1 -1
  49. package/dist/utils/auth.d.ts.map +1 -1
  50. package/dist/utils/auth.js +79 -1
  51. package/dist/utils/auth.js.map +1 -1
  52. package/dist/utils/binary.d.ts.map +1 -1
  53. package/dist/utils/binary.js +10 -0
  54. package/dist/utils/binary.js.map +1 -1
  55. package/dist/utils/instructions.d.ts +36 -12
  56. package/dist/utils/instructions.d.ts.map +1 -1
  57. package/dist/utils/instructions.js +116 -62
  58. package/dist/utils/instructions.js.map +1 -1
  59. package/dist/utils/runtime-homes.d.ts.map +1 -1
  60. package/dist/utils/runtime-homes.js +4 -0
  61. package/dist/utils/runtime-homes.js.map +1 -1
  62. package/dist/utils/skills.d.ts +3 -3
  63. package/dist/utils/skills.d.ts.map +1 -1
  64. package/dist/utils/skills.js +16 -2
  65. package/dist/utils/skills.js.map +1 -1
  66. package/package.json +3 -2
  67. package/src/providers/antigravity/codec.ts +29 -0
  68. package/src/providers/antigravity/connection.ts +304 -0
  69. package/src/providers/antigravity/discovery.ts +90 -0
  70. package/src/providers/antigravity/execute.ts +214 -0
  71. package/src/providers/antigravity/index.ts +48 -0
  72. package/src/providers/antigravity/parse.ts +439 -0
  73. package/src/providers/antigravity/probe.ts +89 -0
  74. package/src/providers/antigravity/runtime.ts +64 -0
  75. package/src/providers/antigravity/session.ts +359 -0
  76. package/src/providers/gemini/index.ts +6 -0
  77. package/src/registry.ts +2 -0
  78. package/src/types.ts +6 -5
  79. package/src/utils/auth.ts +82 -1
  80. package/src/utils/binary.ts +10 -0
  81. package/src/utils/instructions.ts +162 -73
  82. package/src/utils/runtime-homes.ts +4 -0
  83. package/src/utils/skills.ts +17 -4
@@ -29,18 +29,27 @@ export async function resolveInstructions(filePath?: string): Promise<string | n
29
29
  // (.agents/skills + .claude/skills) with a per-runtime "native" escape hatch.
30
30
  // Instruction files follow the same shape:
31
31
  //
32
- // - every runtime except Claude reads AGENTS.md; Claude reads CLAUDE.md
33
- // - Gemini reads GEMINI.md by default (AGENTS.md only when configured), so it
34
- // gets a native escape hatch
32
+ // - every runtime reads AGENTS.md at a workspace root (Claude Code since
33
+ // 2.1.277, where the folder has no CLAUDE.md)
34
+ // - Claude's CLAUDE.md and Gemini's GEMINI.md are native files, written only
35
+ // on opt-in (`includeNativeFiles`)
35
36
  //
36
37
  // Two locations, mirroring installSkills:
37
38
  //
38
39
  // - "workspace": files at {cwd}/ — the repo-root AGENTS.md convention. Files
39
- // dedupe by name, so the default writes CLAUDE.md + AGENTS.md once each.
40
+ // dedupe by name, so the default writes AGENTS.md once.
40
41
  // - "global": each runtime reads its own file in its own home dir
41
42
  // (~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, ...). There
42
43
  // is no universal ~/AGENTS.md, so global is inherently per-runtime.
43
44
  //
45
+ // Claude Code reads AGENTS.md only where no CLAUDE.md exists: a CLAUDE.md (or
46
+ // a CLAUDE.local.md, or one in a parent directory) hides AGENTS.md completely.
47
+ // So a workspace CLAUDE.md is only ever a one-line pointer (`@AGENTS.md`),
48
+ // never a second copy of the brief, and a CLAUDE.md already on disk is
49
+ // reconciled on every install: one holding nothing but our managed region is
50
+ // removed when the opt-in is off, and one the user wrote gets the pointer so
51
+ // the brief still reaches Claude.
52
+ //
44
53
  // Unlike skills (which are symlinked dirs), instruction files carry content, so
45
54
  // installInstructions does a managed-region merge: it wraps `content` in marker
46
55
  // comments and replaces only that region on re-install, preserving anything the
@@ -48,24 +57,32 @@ export async function resolveInstructions(filePath?: string): Promise<string | n
48
57
  // ===========================================================================
49
58
 
50
59
  interface RuntimeInstructionSpec {
51
- /** File this runtime reads at a workspace/repo root. AGENTS.md for all but Claude. */
60
+ /** File this runtime reads at a workspace/repo root. AGENTS.md for every runtime. */
52
61
  projectFile: string;
53
- /** The runtime's own preferred filename. Differs from projectFile only for Gemini (GEMINI.md). */
62
+ /** The runtime's own preferred filename. Written in a workspace only on opt-in (`includeNativeFiles`). */
54
63
  nativeFile: string;
64
+ /**
65
+ * How an opt-in native file carries the brief. "pointer": only an import of
66
+ * the project file (`@AGENTS.md`), because the native file's mere presence
67
+ * hides the project file from the runtime (Claude). "copy": the brief itself
68
+ * (Gemini, which doesn't read AGENTS.md by default).
69
+ */
70
+ nativeFileMode: "pointer" | "copy";
55
71
  /** Whether the runtime has a file-based global config. False for Cursor (global = app User Rules). */
56
72
  hasGlobalFile: boolean;
57
73
  }
58
74
 
59
75
  const RUNTIME_INSTRUCTIONS: Record<SkillRuntime, RuntimeInstructionSpec> = {
60
- claude: { projectFile: "CLAUDE.md", nativeFile: "CLAUDE.md", hasGlobalFile: true },
61
- codex: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", hasGlobalFile: true },
62
- opencode: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", hasGlobalFile: true },
63
- gemini: { projectFile: "AGENTS.md", nativeFile: "GEMINI.md", hasGlobalFile: true },
64
- cursor: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", hasGlobalFile: false },
65
- pi: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", hasGlobalFile: true },
76
+ claude: { projectFile: "AGENTS.md", nativeFile: "CLAUDE.md", nativeFileMode: "pointer", hasGlobalFile: true },
77
+ codex: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", nativeFileMode: "copy", hasGlobalFile: true },
78
+ opencode: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", nativeFileMode: "copy", hasGlobalFile: true },
79
+ gemini: { projectFile: "AGENTS.md", nativeFile: "GEMINI.md", nativeFileMode: "copy", hasGlobalFile: true },
80
+ antigravity: { projectFile: "AGENTS.md", nativeFile: "GEMINI.md", nativeFileMode: "copy", hasGlobalFile: true },
81
+ cursor: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", nativeFileMode: "copy", hasGlobalFile: false },
82
+ pi: { projectFile: "AGENTS.md", nativeFile: "AGENTS.md", nativeFileMode: "copy", hasGlobalFile: true },
66
83
  };
67
84
 
68
- const ALL_RUNTIMES: SkillRuntime[] = ["claude", "codex", "gemini", "cursor", "opencode", "pi"];
85
+ const ALL_RUNTIMES: SkillRuntime[] = ["claude", "codex", "gemini", "antigravity", "cursor", "opencode", "pi"];
69
86
 
70
87
  const DEFAULT_MANAGED_TAG = "agentex";
71
88
 
@@ -78,15 +95,20 @@ export interface InstallInstructionsOptions {
78
95
  cwd?: string;
79
96
  /**
80
97
  * Also write each runtime's native file when it differs from the shared
81
- * standard (currently only Gemini's GEMINI.md). Only affects "workspace";
82
- * "global" always uses native files. Default: false.
98
+ * AGENTS.md: Claude's CLAUDE.md (as a one-line `@AGENTS.md` pointer) and
99
+ * Gemini's GEMINI.md (as a copy). Only affects "workspace"; "global" always
100
+ * uses native files. Default: false.
101
+ *
102
+ * Opt in for Claude Code before 2.1.277, or on Bedrock, Vertex or Foundry,
103
+ * where Claude doesn't read AGENTS.md on its own.
83
104
  */
84
105
  includeNativeFiles?: boolean;
85
106
  /**
86
107
  * Wrap `content` in managed markers and merge into any existing file,
87
108
  * replacing only the previously-managed region and preserving everything the
88
109
  * user wrote outside it. Default: true. When false, the file is overwritten
89
- * with raw `content` (escape hatch for fully-owned files).
110
+ * with raw `content` (escape hatch for fully-owned files), and an existing
111
+ * CLAUDE.md is left alone even though it hides AGENTS.md from Claude Code.
90
112
  */
91
113
  managed?: boolean;
92
114
  /** Marker tag, so the comment reads `<!-- <tag>:managed:start -->`. Default: "agentex". */
@@ -98,7 +120,11 @@ export interface InstallInstructionsOptions {
98
120
  homeDir?: string;
99
121
  }
100
122
 
101
- export type InstructionStatus = "created" | "updated" | "skipped" | "error";
123
+ /**
124
+ * "removed": a workspace CLAUDE.md that held nothing but this installer's
125
+ * managed region, deleted because the opt-in (`includeNativeFiles`) is off.
126
+ */
127
+ export type InstructionStatus = "created" | "updated" | "skipped" | "removed" | "error";
102
128
 
103
129
  export interface InstructionInstallEntry {
104
130
  /** The filename written, e.g. "AGENTS.md", "CLAUDE.md", "GEMINI.md". */
@@ -107,6 +133,8 @@ export interface InstructionInstallEntry {
107
133
  targetPath: string;
108
134
  /** Which requested runtimes this file serves. */
109
135
  runtimes: SkillRuntime[];
136
+ /** Set when the file is a pointer: its managed region is `@<importsFile>`, not the brief. */
137
+ importsFile?: string;
110
138
  status: InstructionStatus;
111
139
  error?: string;
112
140
  }
@@ -116,6 +144,7 @@ export interface InstructionInstallResult {
116
144
  installed: number; // newly created files
117
145
  updated: number; // existing files whose content changed
118
146
  skipped: number; // content already current → no write
147
+ removed: number; // managed-only CLAUDE.md deleted because the opt-in is off
119
148
  errors: number;
120
149
  }
121
150
 
@@ -123,6 +152,8 @@ export interface InstructionTarget {
123
152
  filename: string;
124
153
  targetPath: string;
125
154
  runtimes: SkillRuntime[];
155
+ /** Set when the file is a pointer: its managed region is `@<importsFile>`, not the brief. */
156
+ importsFile?: string;
126
157
  }
127
158
 
128
159
  export interface RemoveInstructionsOptions {
@@ -163,7 +194,8 @@ export interface ManagedBlockOptions {
163
194
  * without touching disk.
164
195
  *
165
196
  * - "workspace": files dedupe by name under {cwd}/ (the default writes
166
- * CLAUDE.md + AGENTS.md once each). `includeNativeFiles` adds GEMINI.md.
197
+ * AGENTS.md once). `includeNativeFiles` adds CLAUDE.md (a pointer, marked by
198
+ * `importsFile`) and GEMINI.md.
167
199
  * - "global": one file per runtime in each runtime's home dir. Runtimes without
168
200
  * a file-based global config (Cursor) are omitted.
169
201
  */
@@ -181,41 +213,42 @@ export function resolveInstructionTargets(options?: {
181
213
  const cwd = options?.cwd;
182
214
  if (!cwd) throw new Error("cwd is required when location is 'workspace'");
183
215
 
184
- const byFile = new Map<string, SkillRuntime[]>();
185
- const add = (filename: string, runtime: SkillRuntime) => {
186
- const list = byFile.get(filename) ?? [];
187
- list.push(runtime);
188
- byFile.set(filename, list);
216
+ const byFile = new Map<string, InstructionTarget>();
217
+ const add = (filename: string, runtime: SkillRuntime, importsFile?: string) => {
218
+ const target: InstructionTarget = byFile.get(filename) ?? {
219
+ filename,
220
+ targetPath: path.join(cwd, filename),
221
+ runtimes: [],
222
+ ...(importsFile !== undefined && { importsFile }),
223
+ };
224
+ target.runtimes.push(runtime);
225
+ byFile.set(filename, target);
189
226
  };
190
227
 
191
228
  for (const runtime of runtimes) {
192
229
  const spec = RUNTIME_INSTRUCTIONS[runtime];
193
230
  add(spec.projectFile, runtime);
194
231
  if (options?.includeNativeFiles && spec.nativeFile !== spec.projectFile) {
195
- add(spec.nativeFile, runtime);
232
+ add(spec.nativeFile, runtime, spec.nativeFileMode === "pointer" ? spec.projectFile : undefined);
196
233
  }
197
234
  }
198
235
 
199
- return [...byFile.entries()].map(([filename, rts]) => ({
200
- filename,
201
- targetPath: path.join(cwd, filename),
202
- runtimes: dedupeRuntimes(rts),
203
- }));
236
+ return [...byFile.values()].map((target) => ({ ...target, runtimes: dedupeRuntimes(target.runtimes) }));
204
237
  }
205
238
 
206
- // global: each runtime reads its own native file in its own home dir.
207
- const targets: InstructionTarget[] = [];
239
+ // global: each runtime reads its own native file in its own home dir. Gemini
240
+ // CLI and Antigravity share ~/.gemini/GEMINI.md, so targets merge by path.
241
+ const byPath = new Map<string, InstructionTarget>();
208
242
  for (const runtime of runtimes) {
209
243
  const spec = RUNTIME_INSTRUCTIONS[runtime];
210
244
  if (!spec.hasGlobalFile) continue; // e.g. cursor global = app User Rules, not a file
211
245
  const home = getDefaultRuntimeHome(runtime, options?.homeDir);
212
- targets.push({
213
- filename: spec.nativeFile,
214
- targetPath: path.join(home, spec.nativeFile),
215
- runtimes: [runtime],
216
- });
246
+ const targetPath = path.join(home, spec.nativeFile);
247
+ const existing = byPath.get(targetPath);
248
+ if (existing) existing.runtimes.push(runtime);
249
+ else byPath.set(targetPath, { filename: spec.nativeFile, targetPath, runtimes: [runtime] });
217
250
  }
218
- return targets;
251
+ return [...byPath.values()];
219
252
  }
220
253
 
221
254
  function dedupeRuntimes(runtimes: SkillRuntime[]): SkillRuntime[] {
@@ -229,18 +262,25 @@ function dedupeRuntimes(runtimes: SkillRuntime[]): SkillRuntime[] {
229
262
  /**
230
263
  * Install an instruction brief into the right per-runtime files.
231
264
  *
232
- * By default merges `content` into a managed region of `{cwd}/CLAUDE.md` and
233
- * `{cwd}/AGENTS.md`, preserving any user-authored content outside the markers.
234
- * Idempotent: a re-install with unchanged content reports every entry as
235
- * "skipped".
265
+ * By default merges `content` into a managed region of `{cwd}/AGENTS.md`,
266
+ * preserving any user-authored content outside the markers. Idempotent: a
267
+ * re-install with unchanged content reports every entry as "skipped".
268
+ *
269
+ * When Claude is among the runtimes, a `{cwd}/CLAUDE.md` already on disk is
270
+ * reconciled, because it would hide AGENTS.md from Claude Code: one holding
271
+ * nothing but this installer's managed region is removed, and one the user
272
+ * wrote gets `@AGENTS.md` in its managed region. With `includeNativeFiles`,
273
+ * CLAUDE.md is written as that pointer either way. `managed: false` leaves an
274
+ * existing CLAUDE.md alone.
236
275
  *
237
276
  * @example
238
277
  * ```ts
239
278
  * // Workspace (repo-root) install — the common case.
240
279
  * await installInstructions(brief, { location: "workspace", cwd: projectDir });
241
- * // → {cwd}/CLAUDE.md + {cwd}/AGENTS.md
280
+ * // → {cwd}/AGENTS.md
242
281
  *
243
- * // Also drop Gemini's native GEMINI.md (it doesn't read AGENTS.md by default).
282
+ * // Also write the native files: CLAUDE.md as an `@AGENTS.md` pointer (for
283
+ * // Claude Code before 2.1.277, or on Bedrock/Vertex/Foundry) and GEMINI.md.
244
284
  * await installInstructions(brief, { location: "workspace", cwd, includeNativeFiles: true });
245
285
  *
246
286
  * // Global install — per-runtime home files.
@@ -254,37 +294,16 @@ export async function installInstructions(
254
294
  ): Promise<InstructionInstallResult> {
255
295
  const managed = options?.managed ?? true;
256
296
  const tag = options?.managedTag ?? DEFAULT_MANAGED_TAG;
257
- const targets = resolveInstructionTargets(options);
258
297
  const entries: InstructionInstallEntry[] = [];
259
298
 
260
- for (const target of targets) {
261
- try {
262
- const existing = await readFileOrNull(target.targetPath);
263
- const next = managed
264
- ? upsertManagedBlock(existing, content, { tag })
265
- : ensureTrailingNewline(content);
266
-
267
- let status: InstructionStatus;
268
- if (existing === null) {
269
- status = "created";
270
- } else if (existing === next) {
271
- status = "skipped";
272
- } else {
273
- status = "updated";
274
- }
275
-
276
- if (status !== "skipped") {
277
- await fs.mkdir(path.dirname(target.targetPath), { recursive: true });
278
- await fs.writeFile(target.targetPath, next, { mode: 0o644 });
279
- }
280
-
281
- entries.push({ ...target, status });
282
- } catch (err) {
283
- entries.push({
284
- ...target,
285
- status: "error",
286
- error: err instanceof Error ? err.message : String(err),
287
- });
299
+ for (const target of resolveInstructionTargets(options)) {
300
+ const body = target.importsFile !== undefined ? `@${target.importsFile}` : content;
301
+ entries.push(await writeTarget(target, body, managed, tag));
302
+ }
303
+ if (managed) {
304
+ for (const target of unrequestedPointerTargets(options)) {
305
+ const entry = await reconcilePointer(target, tag);
306
+ if (entry) entries.push(entry);
288
307
  }
289
308
  }
290
309
 
@@ -293,15 +312,84 @@ export async function installInstructions(
293
312
  installed: entries.filter((e) => e.status === "created").length,
294
313
  updated: entries.filter((e) => e.status === "updated").length,
295
314
  skipped: entries.filter((e) => e.status === "skipped").length,
315
+ removed: entries.filter((e) => e.status === "removed").length,
296
316
  errors: entries.filter((e) => e.status === "error").length,
297
317
  };
298
318
  }
299
319
 
320
+ async function writeTarget(
321
+ target: InstructionTarget,
322
+ body: string,
323
+ managed: boolean,
324
+ tag: string,
325
+ ): Promise<InstructionInstallEntry> {
326
+ try {
327
+ const existing = await readFileOrNull(target.targetPath);
328
+ const next = managed ? upsertManagedBlock(existing, body, { tag }) : ensureTrailingNewline(body);
329
+
330
+ let status: InstructionStatus;
331
+ if (existing === null) {
332
+ status = "created";
333
+ } else if (existing === next) {
334
+ status = "skipped";
335
+ } else {
336
+ status = "updated";
337
+ }
338
+
339
+ if (status !== "skipped") {
340
+ await fs.mkdir(path.dirname(target.targetPath), { recursive: true });
341
+ await fs.writeFile(target.targetPath, next, { mode: 0o644 });
342
+ }
343
+ return { ...target, status };
344
+ } catch (err) {
345
+ return { ...target, status: "error", error: err instanceof Error ? err.message : String(err) };
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Pointer files (a workspace CLAUDE.md) for the requested runtimes that the
351
+ * install doesn't write because the opt-in is off, but that still need
352
+ * reconciling if they exist.
353
+ */
354
+ function unrequestedPointerTargets(options?: InstallInstructionsOptions): InstructionTarget[] {
355
+ if ((options?.location ?? "workspace") !== "workspace" || options?.includeNativeFiles) return [];
356
+ return resolveInstructionTargets({ ...options, includeNativeFiles: true }).filter(
357
+ (target) => target.importsFile !== undefined,
358
+ );
359
+ }
360
+
361
+ /**
362
+ * An existing pointer file with the opt-in off: delete it when nothing but our
363
+ * managed region is in it, otherwise keep the user's content and make sure it
364
+ * imports the project file. Returns null when there is no file.
365
+ */
366
+ async function reconcilePointer(
367
+ target: InstructionTarget,
368
+ tag: string,
369
+ ): Promise<InstructionInstallEntry | null> {
370
+ try {
371
+ const existing = await readFileOrNull(target.targetPath);
372
+ if (existing === null) return null;
373
+ if (stripManagedBlock(existing, { tag }) === null) {
374
+ await fs.rm(target.targetPath, { force: true });
375
+ return { ...target, status: "removed" };
376
+ }
377
+ const next = upsertManagedBlock(existing, `@${target.importsFile}`, { tag });
378
+ if (next === existing) return { ...target, status: "skipped" };
379
+ await fs.writeFile(target.targetPath, next);
380
+ return { ...target, status: "updated" };
381
+ } catch (err) {
382
+ return { ...target, status: "error", error: err instanceof Error ? err.message : String(err) };
383
+ }
384
+ }
385
+
300
386
  /**
301
387
  * Remove the managed region installed by {@link installInstructions}, preserving
302
388
  * any user-authored content outside the markers. If the file contains nothing
303
389
  * but the managed block, it is deleted. User-owned files (no managed region) are
304
- * left untouched and reported as "skipped".
390
+ * left untouched and reported as "skipped". Native files (CLAUDE.md, GEMINI.md)
391
+ * are always checked, whether or not they were installed with
392
+ * `includeNativeFiles`.
305
393
  */
306
394
  export async function removeInstructions(
307
395
  options?: RemoveInstructionsOptions,
@@ -311,6 +399,7 @@ export async function removeInstructions(
311
399
  runtimes: options?.runtimes,
312
400
  location: options?.location,
313
401
  cwd: options?.cwd,
402
+ includeNativeFiles: true,
314
403
  homeDir: options?.homeDir,
315
404
  });
316
405
  const entries: InstructionRemoveEntry[] = [];
@@ -10,6 +10,7 @@ const ENV_VAR_MAP: Record<SkillRuntime, string | null> = {
10
10
  claude: "CLAUDE_CONFIG_DIR",
11
11
  codex: "CODEX_HOME",
12
12
  gemini: "GEMINI_CONFIG_DIR",
13
+ antigravity: null, // agy has no home override
13
14
  cursor: "CURSOR_CONFIG_DIR",
14
15
  opencode: "XDG_CONFIG_HOME", // affects ~/.config/opencode/
15
16
  pi: "PI_HOME",
@@ -22,6 +23,9 @@ const HOME_SUBPATH_MAP: Record<SkillRuntime, string[]> = {
22
23
  claude: [".claude"],
23
24
  codex: [".codex"],
24
25
  gemini: [".gemini"],
26
+ // Shared with Gemini CLI: agy reads its global GEMINI.md from ~/.gemini and
27
+ // keeps its own settings and skills in ~/.gemini/antigravity-cli.
28
+ antigravity: [".gemini"],
25
29
  cursor: [".cursor"],
26
30
  opencode: [".config", "opencode"],
27
31
  pi: [".pi"],
@@ -2,13 +2,13 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import * as os from "node:os";
4
4
 
5
- export type SkillRuntime = "claude" | "codex" | "gemini" | "cursor" | "opencode" | "pi";
5
+ export type SkillRuntime = "claude" | "codex" | "gemini" | "antigravity" | "cursor" | "opencode" | "pi";
6
6
 
7
7
  export type SkillLocation = "global" | "workspace";
8
8
 
9
9
  /**
10
10
  * The two standard skill discovery channels:
11
- * - "agents": ~/.agents/skills/ or {cwd}/.agents/skills/ — scanned by Codex, Gemini, Cursor, OpenCode, Pi
11
+ * - "agents": ~/.agents/skills/ or {cwd}/.agents/skills/ — scanned by Codex, Gemini, Antigravity, Cursor, OpenCode, Pi
12
12
  * - "claude": ~/.claude/skills/ or {cwd}/.claude/skills/ — scanned by Claude Code (the only agent that doesn't scan .agents/)
13
13
  */
14
14
  export type SkillChannel = "agents" | "claude";
@@ -23,6 +23,12 @@ const NATIVE_DIRS: Record<string, { global: string; workspace: (cwd: string) =>
23
23
  global: path.join(os.homedir(), ".gemini", "skills"),
24
24
  workspace: (cwd) => path.join(cwd, ".gemini", "skills"),
25
25
  },
26
+ // Antigravity reads workspace skills from the standard .agents/skills
27
+ // channel; only its global folder is its own.
28
+ antigravity: {
29
+ global: path.join(os.homedir(), ".gemini", "antigravity-cli", "skills"),
30
+ workspace: (cwd) => path.join(cwd, ".agents", "skills"),
31
+ },
26
32
  cursor: {
27
33
  global: path.join(os.homedir(), ".cursor", "skills"),
28
34
  workspace: (cwd) => path.join(cwd, ".cursor", "skills"),
@@ -139,7 +145,14 @@ function resolveTargets(
139
145
  ): Array<{ target: string; skillsHome: string }> {
140
146
  const targets = resolveStandardPaths(location, cwd);
141
147
  if (includeNativeDirs) {
142
- targets.push(...resolveNativePaths(location, cwd));
148
+ // A runtime whose native folder is a standard channel (Antigravity's
149
+ // workspace .agents/skills) is already covered by that channel.
150
+ const seen = new Set(targets.map((t) => t.skillsHome));
151
+ for (const native of resolveNativePaths(location, cwd)) {
152
+ if (seen.has(native.skillsHome)) continue;
153
+ seen.add(native.skillsHome);
154
+ targets.push(native);
155
+ }
143
156
  }
144
157
  return targets;
145
158
  }
@@ -152,7 +165,7 @@ function resolveTargets(
152
165
  * Install skill directories into agent discovery paths via idempotent symlinks.
153
166
  *
154
167
  * By default, installs into the two standard channels:
155
- * - `.agents/skills/` — discovered by Codex, Gemini, Cursor, OpenCode, Pi
168
+ * - `.agents/skills/` — discovered by Codex, Gemini, Antigravity, Cursor, OpenCode, Pi
156
169
  * - `.claude/skills/` — discovered by Claude Code
157
170
  *
158
171
  * Set `includeNativeDirs: true` to also install into each runtime's