openusage 0.1.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 (58) hide show
  1. package/bin/openusage +91 -0
  2. package/package.json +33 -0
  3. package/plugins/amp/icon.svg +6 -0
  4. package/plugins/amp/plugin.js +175 -0
  5. package/plugins/amp/plugin.json +20 -0
  6. package/plugins/amp/plugin.test.js +365 -0
  7. package/plugins/antigravity/icon.svg +3 -0
  8. package/plugins/antigravity/plugin.js +484 -0
  9. package/plugins/antigravity/plugin.json +17 -0
  10. package/plugins/antigravity/plugin.test.js +1356 -0
  11. package/plugins/claude/icon.svg +3 -0
  12. package/plugins/claude/plugin.js +565 -0
  13. package/plugins/claude/plugin.json +28 -0
  14. package/plugins/claude/plugin.test.js +1012 -0
  15. package/plugins/codex/icon.svg +3 -0
  16. package/plugins/codex/plugin.js +673 -0
  17. package/plugins/codex/plugin.json +30 -0
  18. package/plugins/codex/plugin.test.js +1071 -0
  19. package/plugins/copilot/icon.svg +3 -0
  20. package/plugins/copilot/plugin.js +264 -0
  21. package/plugins/copilot/plugin.json +20 -0
  22. package/plugins/copilot/plugin.test.js +529 -0
  23. package/plugins/cursor/icon.svg +3 -0
  24. package/plugins/cursor/plugin.js +526 -0
  25. package/plugins/cursor/plugin.json +24 -0
  26. package/plugins/cursor/plugin.test.js +1168 -0
  27. package/plugins/factory/icon.svg +1 -0
  28. package/plugins/factory/plugin.js +407 -0
  29. package/plugins/factory/plugin.json +19 -0
  30. package/plugins/factory/plugin.test.js +833 -0
  31. package/plugins/gemini/icon.svg +4 -0
  32. package/plugins/gemini/plugin.js +413 -0
  33. package/plugins/gemini/plugin.json +20 -0
  34. package/plugins/gemini/plugin.test.js +735 -0
  35. package/plugins/jetbrains-ai-assistant/icon.svg +3 -0
  36. package/plugins/jetbrains-ai-assistant/plugin.js +357 -0
  37. package/plugins/jetbrains-ai-assistant/plugin.json +17 -0
  38. package/plugins/jetbrains-ai-assistant/plugin.test.js +338 -0
  39. package/plugins/kimi/icon.svg +3 -0
  40. package/plugins/kimi/plugin.js +358 -0
  41. package/plugins/kimi/plugin.json +19 -0
  42. package/plugins/kimi/plugin.test.js +619 -0
  43. package/plugins/minimax/icon.svg +4 -0
  44. package/plugins/minimax/plugin.js +388 -0
  45. package/plugins/minimax/plugin.json +17 -0
  46. package/plugins/minimax/plugin.test.js +943 -0
  47. package/plugins/perplexity/icon.svg +1 -0
  48. package/plugins/perplexity/plugin.js +378 -0
  49. package/plugins/perplexity/plugin.json +15 -0
  50. package/plugins/perplexity/plugin.test.js +602 -0
  51. package/plugins/windsurf/icon.svg +3 -0
  52. package/plugins/windsurf/plugin.js +218 -0
  53. package/plugins/windsurf/plugin.json +16 -0
  54. package/plugins/windsurf/plugin.test.js +455 -0
  55. package/plugins/zai/icon.svg +5 -0
  56. package/plugins/zai/plugin.js +156 -0
  57. package/plugins/zai/plugin.json +18 -0
  58. package/plugins/zai/plugin.test.js +396 -0
@@ -0,0 +1,484 @@
1
+ (function () {
2
+ var LS_SERVICE = "exa.language_server_pb.LanguageServerService"
3
+ var STATE_DB = "~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb"
4
+ var CLOUD_CODE_URLS = [
5
+ "https://daily-cloudcode-pa.googleapis.com",
6
+ "https://cloudcode-pa.googleapis.com",
7
+ ]
8
+ var FETCH_MODELS_PATH = "/v1internal:fetchAvailableModels"
9
+ var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token"
10
+ var GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
11
+ var GOOGLE_CLIENT_SECRET = "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"
12
+ var CC_MODEL_BLACKLIST = {
13
+ "MODEL_CHAT_20706": true,
14
+ "MODEL_CHAT_23310": true,
15
+ "MODEL_GOOGLE_GEMINI_2_5_FLASH": true,
16
+ "MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING": true,
17
+ "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE": true,
18
+ "MODEL_GOOGLE_GEMINI_2_5_PRO": true,
19
+ "MODEL_PLACEHOLDER_M19": true,
20
+ "MODEL_PLACEHOLDER_M9": true,
21
+ "MODEL_PLACEHOLDER_M12": true,
22
+ }
23
+ // --- Protobuf wire-format decoder ---
24
+
25
+ function readVarint(s, pos) {
26
+ var v = 0
27
+ var shift = 0
28
+ while (pos < s.length) {
29
+ var b = s.charCodeAt(pos++)
30
+ v += (b & 0x7f) * Math.pow(2, shift)
31
+ if ((b & 0x80) === 0) return { v: v, p: pos }
32
+ shift += 7
33
+ }
34
+ return null
35
+ }
36
+
37
+ function readFields(s) {
38
+ var fields = {}
39
+ var pos = 0
40
+ while (pos < s.length) {
41
+ var tag = readVarint(s, pos)
42
+ if (!tag) break
43
+ pos = tag.p
44
+ var fieldNum = Math.floor(tag.v / 8)
45
+ var wireType = tag.v % 8
46
+ if (wireType === 0) {
47
+ var val = readVarint(s, pos)
48
+ if (!val) break
49
+ fields[fieldNum] = { type: 0, value: val.v }
50
+ pos = val.p
51
+ } else if (wireType === 2) {
52
+ var len = readVarint(s, pos)
53
+ if (!len) break
54
+ pos = len.p
55
+ fields[fieldNum] = { type: 2, data: s.substring(pos, pos + len.v) }
56
+ pos += len.v
57
+ } else {
58
+ break
59
+ }
60
+ }
61
+ return fields
62
+ }
63
+
64
+ // --- SQLite credential reading ---
65
+
66
+ function loadApiKey(ctx) {
67
+ try {
68
+ var rows = ctx.host.sqlite.query(
69
+ STATE_DB,
70
+ "SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus' LIMIT 1"
71
+ )
72
+ var parsed = ctx.util.tryParseJson(rows)
73
+ if (!parsed || !parsed.length || !parsed[0].value) return null
74
+ var auth = ctx.util.tryParseJson(parsed[0].value)
75
+ if (!auth || !auth.apiKey) return null
76
+ return auth.apiKey
77
+ } catch (e) {
78
+ ctx.host.log.warn("failed to read auth from antigravity DB: " + String(e))
79
+ return null
80
+ }
81
+ }
82
+
83
+ function loadProtoTokens(ctx) {
84
+ try {
85
+ var rows = ctx.host.sqlite.query(
86
+ STATE_DB,
87
+ "SELECT value FROM ItemTable WHERE key = 'jetskiStateSync.agentManagerInitState' LIMIT 1"
88
+ )
89
+ var parsed = ctx.util.tryParseJson(rows)
90
+ if (!parsed || !parsed.length || !parsed[0].value) return null
91
+ var raw = ctx.base64.decode(parsed[0].value)
92
+ var outer = readFields(raw)
93
+ if (!outer[6] || outer[6].type !== 2) return null
94
+ var inner = readFields(outer[6].data)
95
+ var accessToken = (inner[1] && inner[1].type === 2) ? inner[1].data : null
96
+ var refreshToken = (inner[3] && inner[3].type === 2) ? inner[3].data : null
97
+ var expirySeconds = null
98
+ if (inner[4] && inner[4].type === 2) {
99
+ var ts = readFields(inner[4].data)
100
+ if (ts[1] && ts[1].type === 0) expirySeconds = ts[1].value
101
+ }
102
+ if (!accessToken) return null
103
+ return { accessToken: accessToken, refreshToken: refreshToken, expirySeconds: expirySeconds }
104
+ } catch (e) {
105
+ ctx.host.log.warn("failed to read proto tokens from antigravity DB: " + String(e))
106
+ return null
107
+ }
108
+ }
109
+
110
+ // --- Google OAuth token refresh ---
111
+
112
+ function refreshAccessToken(ctx, refreshTokenValue) {
113
+ if (!refreshTokenValue) {
114
+ ctx.host.log.warn("refresh skipped: no refresh token")
115
+ return null
116
+ }
117
+ ctx.host.log.info("attempting Google OAuth token refresh")
118
+ try {
119
+ var resp = ctx.host.http.request({
120
+ method: "POST",
121
+ url: GOOGLE_OAUTH_URL,
122
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
123
+ bodyText:
124
+ "client_id=" + encodeURIComponent(GOOGLE_CLIENT_ID) +
125
+ "&client_secret=" + encodeURIComponent(GOOGLE_CLIENT_SECRET) +
126
+ "&refresh_token=" + encodeURIComponent(refreshTokenValue) +
127
+ "&grant_type=refresh_token",
128
+ timeoutMs: 15000,
129
+ })
130
+ if (resp.status < 200 || resp.status >= 300) {
131
+ ctx.host.log.warn("Google OAuth refresh returned status: " + resp.status)
132
+ return null
133
+ }
134
+ var body = ctx.util.tryParseJson(resp.bodyText)
135
+ if (!body || !body.access_token) {
136
+ ctx.host.log.warn("Google OAuth refresh response missing access_token")
137
+ return null
138
+ }
139
+ var expiresIn = (typeof body.expires_in === "number") ? body.expires_in : 3600
140
+ cacheToken(ctx, body.access_token, expiresIn)
141
+ return body.access_token
142
+ } catch (e) {
143
+ ctx.host.log.warn("Google OAuth refresh failed: " + String(e))
144
+ return null
145
+ }
146
+ }
147
+
148
+ // --- Token cache ---
149
+
150
+ function loadCachedToken(ctx) {
151
+ var path = ctx.app.pluginDataDir + "/auth.json"
152
+ try {
153
+ if (!ctx.host.fs.exists(path)) return null
154
+ var data = ctx.util.tryParseJson(ctx.host.fs.readText(path))
155
+ if (!data || !data.accessToken || !data.expiresAtMs) return null
156
+ if (data.expiresAtMs <= Date.now()) return null
157
+ return data.accessToken
158
+ } catch (e) {
159
+ ctx.host.log.warn("failed to read cached token: " + String(e))
160
+ return null
161
+ }
162
+ }
163
+
164
+ function cacheToken(ctx, accessToken, expiresInSeconds) {
165
+ var path = ctx.app.pluginDataDir + "/auth.json"
166
+ try {
167
+ ctx.host.fs.writeText(path, JSON.stringify({
168
+ accessToken: accessToken,
169
+ expiresAtMs: Date.now() + (expiresInSeconds || 3600) * 1000,
170
+ }))
171
+ } catch (e) {
172
+ ctx.host.log.warn("failed to cache refreshed token: " + String(e))
173
+ }
174
+ }
175
+
176
+ // --- LS discovery ---
177
+
178
+ function discoverLs(ctx) {
179
+ return ctx.host.ls.discover({
180
+ processName: "language_server_macos",
181
+ markers: ["antigravity"],
182
+ csrfFlag: "--csrf_token",
183
+ portFlag: "--extension_server_port",
184
+ })
185
+ }
186
+
187
+ function probePort(ctx, scheme, port, csrf) {
188
+ ctx.host.http.request({
189
+ method: "POST",
190
+ url: scheme + "://127.0.0.1:" + port + "/" + LS_SERVICE + "/GetUnleashData",
191
+ headers: {
192
+ "Content-Type": "application/json",
193
+ "Connect-Protocol-Version": "1",
194
+ "x-codeium-csrf-token": csrf,
195
+ },
196
+ bodyText: JSON.stringify({
197
+ context: {
198
+ properties: {
199
+ devMode: "false",
200
+ extensionVersion: "unknown",
201
+ ide: "antigravity",
202
+ ideVersion: "unknown",
203
+ os: "macos",
204
+ },
205
+ },
206
+ }),
207
+ timeoutMs: 5000,
208
+ dangerouslyIgnoreTls: scheme === "https",
209
+ })
210
+ // Any HTTP response means this port is alive (even 400 validation errors).
211
+ return true
212
+ }
213
+
214
+ function findWorkingPort(ctx, discovery) {
215
+ var ports = discovery.ports || []
216
+ for (var i = 0; i < ports.length; i++) {
217
+ var port = ports[i]
218
+ // Try HTTPS first (LS may use self-signed cert), then HTTP
219
+ try { if (probePort(ctx, "https", port, discovery.csrf)) return { port: port, scheme: "https" } } catch (e) { /* ignore */ }
220
+ try { if (probePort(ctx, "http", port, discovery.csrf)) return { port: port, scheme: "http" } } catch (e) { /* ignore */ }
221
+ ctx.host.log.info("port " + port + " probe failed on both schemes")
222
+ }
223
+ if (discovery.extensionPort) return { port: discovery.extensionPort, scheme: "http" }
224
+ return null
225
+ }
226
+
227
+ function callLs(ctx, port, scheme, csrf, method, body) {
228
+ var resp = ctx.host.http.request({
229
+ method: "POST",
230
+ url: scheme + "://127.0.0.1:" + port + "/" + LS_SERVICE + "/" + method,
231
+ headers: {
232
+ "Content-Type": "application/json",
233
+ "Connect-Protocol-Version": "1",
234
+ "x-codeium-csrf-token": csrf,
235
+ },
236
+ bodyText: JSON.stringify(body || {}),
237
+ timeoutMs: 10000,
238
+ dangerouslyIgnoreTls: scheme === "https",
239
+ })
240
+ if (resp.status < 200 || resp.status >= 300) {
241
+ ctx.host.log.warn("callLs " + method + " returned " + resp.status)
242
+ return null
243
+ }
244
+ return ctx.util.tryParseJson(resp.bodyText)
245
+ }
246
+
247
+ // --- Line builders ---
248
+
249
+ function normalizeLabel(label) {
250
+ // "Gemini 3 Pro (High)" -> "Gemini 3 Pro"
251
+ return label.replace(/\s*\([^)]*\)\s*$/, "").trim()
252
+ }
253
+
254
+ function poolLabel(normalizedLabel) {
255
+ var lower = normalizedLabel.toLowerCase()
256
+ if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "Gemini Pro"
257
+ if (lower.indexOf("gemini") !== -1 && lower.indexOf("flash") !== -1) return "Gemini Flash"
258
+ // All non-Gemini models (Claude, GPT-OSS, etc.) share a single quota pool
259
+ return "Claude"
260
+ }
261
+
262
+ function modelSortKey(label) {
263
+ var lower = label.toLowerCase()
264
+ // Gemini Pro variants first, then other Gemini, then Claude Opus, then other Claude, then rest
265
+ if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "0a_" + label
266
+ if (lower.indexOf("gemini") !== -1) return "0b_" + label
267
+ if (lower.indexOf("claude") !== -1 && lower.indexOf("opus") !== -1) return "1a_" + label
268
+ if (lower.indexOf("claude") !== -1) return "1b_" + label
269
+ return "2_" + label
270
+ }
271
+
272
+ var QUOTA_PERIOD_MS = 5 * 60 * 60 * 1000 // 5 hours
273
+
274
+ function modelLine(ctx, label, remainingFraction, resetTime) {
275
+ var clamped = Math.max(0, Math.min(1, remainingFraction))
276
+ var used = Math.round((1 - clamped) * 100)
277
+ return ctx.line.progress({
278
+ label: label,
279
+ used: used,
280
+ limit: 100,
281
+ format: { kind: "percent" },
282
+ resetsAt: resetTime || undefined,
283
+ periodDurationMs: QUOTA_PERIOD_MS,
284
+ })
285
+ }
286
+
287
+ function buildModelLines(ctx, configs) {
288
+ var deduped = {}
289
+ for (var i = 0; i < configs.length; i++) {
290
+ var c = configs[i]
291
+ var label = (typeof c.label === "string") ? c.label.trim() : ""
292
+ if (!label) continue
293
+ var qi = c.quotaInfo
294
+ var frac = (qi && typeof qi.remainingFraction === "number") ? qi.remainingFraction : 0
295
+ var rtime = (qi && qi.resetTime) || undefined
296
+ var pool = poolLabel(normalizeLabel(label))
297
+ if (!deduped[pool] || frac < deduped[pool].remainingFraction) {
298
+ deduped[pool] = {
299
+ label: pool,
300
+ remainingFraction: frac,
301
+ resetTime: rtime,
302
+ }
303
+ }
304
+ }
305
+
306
+ var models = []
307
+ var keys = Object.keys(deduped)
308
+ for (var i = 0; i < keys.length; i++) {
309
+ var m = deduped[keys[i]]
310
+ m.sortKey = modelSortKey(m.label)
311
+ models.push(m)
312
+ }
313
+
314
+ models.sort(function (a, b) {
315
+ return a.sortKey < b.sortKey ? -1 : a.sortKey > b.sortKey ? 1 : 0
316
+ })
317
+
318
+ var lines = []
319
+ for (var i = 0; i < models.length; i++) {
320
+ lines.push(modelLine(ctx, models[i].label, models[i].remainingFraction, models[i].resetTime))
321
+ }
322
+ return lines
323
+ }
324
+
325
+ // --- Cloud Code API ---
326
+
327
+ function probeCloudCode(ctx, token) {
328
+ for (var i = 0; i < CLOUD_CODE_URLS.length; i++) {
329
+ try {
330
+ var resp = ctx.host.http.request({
331
+ method: "POST",
332
+ url: CLOUD_CODE_URLS[i] + FETCH_MODELS_PATH,
333
+ headers: {
334
+ "Content-Type": "application/json",
335
+ Authorization: "Bearer " + token,
336
+ "User-Agent": "antigravity",
337
+ },
338
+ bodyText: "{}",
339
+ timeoutMs: 15000,
340
+ })
341
+ if (ctx.util.isAuthStatus(resp.status)) return { _authFailed: true }
342
+ if (resp.status >= 200 && resp.status < 300) {
343
+ return ctx.util.tryParseJson(resp.bodyText)
344
+ }
345
+ } catch (e) {
346
+ ctx.host.log.warn("Cloud Code request failed (" + CLOUD_CODE_URLS[i] + "): " + String(e))
347
+ }
348
+ }
349
+ return null
350
+ }
351
+
352
+ function parseCloudCodeModels(data) {
353
+ var modelsObj = data && data.models
354
+ if (!modelsObj || typeof modelsObj !== "object") return []
355
+ var keys = Object.keys(modelsObj)
356
+ var configs = []
357
+ for (var i = 0; i < keys.length; i++) {
358
+ var m = modelsObj[keys[i]]
359
+ if (!m || typeof m !== "object") continue
360
+ if (m.isInternal) continue
361
+ var modelId = m.model || keys[i]
362
+ if (CC_MODEL_BLACKLIST[modelId]) continue
363
+ var displayName = (typeof m.displayName === "string") ? m.displayName.trim() : ""
364
+ if (!displayName) continue
365
+ var qi = m.quotaInfo
366
+ var frac = (qi && typeof qi.remainingFraction === "number") ? qi.remainingFraction : 0
367
+ var rtime = (qi && qi.resetTime) || undefined
368
+ configs.push({
369
+ label: displayName,
370
+ quotaInfo: { remainingFraction: frac, resetTime: rtime },
371
+ })
372
+ }
373
+ return configs
374
+ }
375
+
376
+ // --- LS probe ---
377
+
378
+ function probeLs(ctx, apiKey) {
379
+ var discovery = discoverLs(ctx)
380
+ if (!discovery) return null
381
+
382
+ var found = findWorkingPort(ctx, discovery)
383
+ if (!found) return null
384
+
385
+ ctx.host.log.info("using LS at " + found.scheme + "://127.0.0.1:" + found.port)
386
+
387
+ var metadata = {
388
+ ideName: "antigravity",
389
+ extensionName: "antigravity",
390
+ ideVersion: "unknown",
391
+ locale: "en",
392
+ }
393
+ if (apiKey) metadata.apiKey = apiKey
394
+
395
+ // Try GetUserStatus first, fall back to GetCommandModelConfigs
396
+ var data = null
397
+ try {
398
+ data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetUserStatus", { metadata: metadata })
399
+ } catch (e) {
400
+ ctx.host.log.warn("GetUserStatus threw: " + String(e))
401
+ }
402
+ var hasUserStatus = data && data.userStatus
403
+
404
+ if (!hasUserStatus) {
405
+ ctx.host.log.warn("GetUserStatus failed, trying GetCommandModelConfigs")
406
+ data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetCommandModelConfigs", { metadata: metadata })
407
+ }
408
+
409
+ // Parse model configs
410
+ var configs
411
+ if (hasUserStatus) {
412
+ configs = (data.userStatus.cascadeModelConfigData || {}).clientModelConfigs || []
413
+ } else if (data && data.clientModelConfigs) {
414
+ configs = data.clientModelConfigs
415
+ } else {
416
+ return null
417
+ }
418
+
419
+ var filtered = []
420
+ for (var j = 0; j < configs.length; j++) {
421
+ var mid = configs[j].modelOrAlias && configs[j].modelOrAlias.model
422
+ if (mid && CC_MODEL_BLACKLIST[mid]) continue
423
+ filtered.push(configs[j])
424
+ }
425
+
426
+ var lines = buildModelLines(ctx, filtered)
427
+ if (lines.length === 0) return null
428
+
429
+ var plan = null
430
+ if (hasUserStatus) {
431
+ var ps = data.userStatus.planStatus || {}
432
+ var pi = ps.planInfo || {}
433
+ plan = pi.planName || null
434
+ }
435
+
436
+ return { plan: plan, lines: lines }
437
+ }
438
+
439
+ // --- Probe ---
440
+
441
+ function probe(ctx) {
442
+ var apiKey = loadApiKey(ctx)
443
+ var proto = loadProtoTokens(ctx)
444
+
445
+ var lsResult = probeLs(ctx, apiKey)
446
+ if (lsResult) return lsResult
447
+
448
+ var tokens = []
449
+ if (proto && proto.accessToken) {
450
+ if (!proto.expirySeconds || proto.expirySeconds > Math.floor(Date.now() / 1000)) {
451
+ tokens.push(proto.accessToken)
452
+ }
453
+ }
454
+
455
+ var cached = loadCachedToken(ctx)
456
+ if (cached && cached !== (proto && proto.accessToken)) tokens.push(cached)
457
+
458
+ if (apiKey && apiKey !== (proto && proto.accessToken) && apiKey !== cached) tokens.push(apiKey)
459
+
460
+ if (tokens.length === 0) throw "Start Antigravity and try again."
461
+
462
+ var ccData = null
463
+ for (var i = 0; i < tokens.length; i++) {
464
+ ccData = probeCloudCode(ctx, tokens[i])
465
+ if (ccData && !ccData._authFailed) break
466
+ ccData = null
467
+ }
468
+
469
+ if (!ccData && proto && proto.refreshToken) {
470
+ var refreshed = refreshAccessToken(ctx, proto.refreshToken)
471
+ if (refreshed) ccData = probeCloudCode(ctx, refreshed)
472
+ }
473
+
474
+ if (ccData && !ccData._authFailed) {
475
+ var configs = parseCloudCodeModels(ccData)
476
+ var lines = buildModelLines(ctx, configs)
477
+ if (lines.length > 0) return { plan: null, lines: lines }
478
+ }
479
+
480
+ throw "Start Antigravity and try again."
481
+ }
482
+
483
+ globalThis.__openusage_plugin = { id: "antigravity", probe: probe }
484
+ })()
@@ -0,0 +1,17 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "antigravity",
4
+ "name": "Antigravity",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "icon": "icon.svg",
8
+ "brandColor": "#4285F4",
9
+ "cli": {
10
+ "category": "ide"
11
+ },
12
+ "lines": [
13
+ { "type": "progress", "label": "Gemini Pro", "scope": "overview", "primaryOrder": 1 },
14
+ { "type": "progress", "label": "Gemini Flash", "scope": "overview" },
15
+ { "type": "progress", "label": "Claude", "scope": "overview" }
16
+ ]
17
+ }