@aiwg/cli 2026.9.2 → 2026.9.3

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 (67) hide show
  1. package/agentic/code/providers/antigravity/provider-contract.v1.json +121 -0
  2. package/agentic/code/providers/capability-matrix.yaml +87 -1
  3. package/agentic/code/providers/model-capabilities.v1.json +31 -0
  4. package/agentic/code/providers/model-catalog.v1.json +29 -0
  5. package/agentic/code/providers/omp/README.md +58 -0
  6. package/agentic/code/providers/omp/aiwg-bridge.ts +52 -0
  7. package/dist/src/agents/agent-deployer.js +18 -0
  8. package/dist/src/agents/agent-packager.js +25 -0
  9. package/dist/src/artifacts/backends/sqlite-backend.js +18 -10
  10. package/dist/src/artifacts/query-engine.js +30 -0
  11. package/dist/src/cli/agent-spawn.js +13 -2
  12. package/dist/src/cli/handlers/help.js +3 -1
  13. package/dist/src/cli/handlers/init.js +2 -0
  14. package/dist/src/cli/handlers/models.js +1 -1
  15. package/dist/src/cli/handlers/runtime-info.js +8 -1
  16. package/dist/src/cli/handlers/session.js +5 -4
  17. package/dist/src/cli/handlers/sessions.js +26 -9
  18. package/dist/src/cli/handlers/setup.js +9 -2
  19. package/dist/src/cli/handlers/steward.js +13 -2
  20. package/dist/src/cli/handlers/subcommands.js +11 -0
  21. package/dist/src/cli/handlers/team.js +68 -7
  22. package/dist/src/cli/handlers/use.js +121 -9
  23. package/dist/src/cli/scope-resolver.js +7 -0
  24. package/dist/src/config/aiwg-config.js +1 -0
  25. package/dist/src/dataset/fortemi-live-qualification.d.ts +53 -0
  26. package/dist/src/dataset/fortemi-live-qualification.js +297 -0
  27. package/dist/src/dataset/index.d.ts +1 -0
  28. package/dist/src/dataset/index.js +1 -0
  29. package/dist/src/mcp/cli.mjs +30 -1
  30. package/dist/src/mcp/omp-config.mjs +128 -0
  31. package/dist/src/mcp/registry.js +45 -5
  32. package/dist/src/mcp/registry.mjs +29 -6
  33. package/dist/src/models/model-capabilities.v1.json +31 -0
  34. package/dist/src/models/model-catalog.v1.json +29 -0
  35. package/dist/src/models/model-discovery.js +46 -5
  36. package/dist/src/models/provider-policy.js +5 -3
  37. package/dist/src/plugin/skill-command-translator.js +2 -0
  38. package/dist/src/providers/capability-matrix.yaml +87 -1
  39. package/dist/src/providers/omp-agent.mjs +40 -0
  40. package/dist/src/providers/omp-diagnostics.mjs +15 -0
  41. package/dist/src/providers/omp-paths.mjs +38 -0
  42. package/dist/src/providers/provider-definitions.js +83 -0
  43. package/dist/src/providers/provider-definitions.mjs +25 -1
  44. package/dist/src/providers/provider-inventory.js +2 -0
  45. package/dist/src/sessions/adapters/omp.js +203 -0
  46. package/dist/src/sessions/batch-import.js +7 -0
  47. package/dist/src/sessions/contracts.js +1 -1
  48. package/dist/src/sessions/importer.js +4 -3
  49. package/dist/src/sessions/index.js +1 -0
  50. package/dist/src/sessions/readers.js +4 -3
  51. package/dist/src/sessions/workspace-discovery.js +12 -2
  52. package/dist/src/skills/deployer.js +21 -1
  53. package/dist/src/smiths/agentsmith/generator.js +1 -0
  54. package/dist/src/smiths/context-pipeline/parallelism-section.js +2 -0
  55. package/dist/src/smiths/context-pipeline/provider-policy.js +2 -2
  56. package/dist/src/smiths/context-pipeline/workspace-context.js +2 -2
  57. package/dist/src/storage/backends/fortemi.js +142 -17
  58. package/dist/src/storage/fortemi-qualification-receipt.js +206 -0
  59. package/dist/src/storage/fortemi-qualification.js +67 -6
  60. package/dist/src/storage/index.js +1 -0
  61. package/package.json +2 -1
  62. package/schemas/dataset/fortemi-live-qualification-receipt.v1.schema.json +132 -0
  63. package/tools/agents/deploy-agents.mjs +6 -3
  64. package/tools/agents/providers/antigravity.mjs +147 -0
  65. package/tools/agents/providers/omp.d.mts +4 -0
  66. package/tools/agents/providers/omp.mjs +256 -0
  67. package/tools/providers/antigravity-transport.mjs +124 -0
@@ -1,3 +1,5 @@
1
+ import { manageOmpMcp } from './omp-config.mjs';
2
+ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
1
3
  /**
2
4
  * MCP Server Registry
3
5
  *
@@ -156,6 +158,12 @@ export class McpServerRegistry {
156
158
  function buildServerConfig(server, provider) {
157
159
  const adapter = getProviderDefinition(provider)?.adapters.mcpInjection;
158
160
  switch (adapter) {
161
+ case 'antigravity': {
162
+ if (server.type === 'stdio') {
163
+ return { command: server.command, args: server.args || [], ...(server.env ? { env: server.env } : {}) };
164
+ }
165
+ return { serverUrl: server.url, ...(server.headers ? { headers: server.headers } : {}) };
166
+ }
159
167
  case 'claude-code': {
160
168
  if (server.type === 'stdio') {
161
169
  return {
@@ -258,9 +266,21 @@ function buildServerToml(server) {
258
266
  /**
259
267
  * Get the config file path for a provider.
260
268
  */
261
- export function getProviderConfigPath(provider, projectDir = '.') {
269
+ export function getProviderConfigPath(provider, projectDir = '.', options = {}) {
270
+ if ((provider === 'antigravity' || provider === 'agy') && options.scope === 'user') {
271
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
272
+ return resolve(homeDir, '.gemini/config/mcp_config.json');
273
+ }
274
+ if ((provider === 'omp' || provider === 'oh-my-pi') && options.scope !== undefined && !['user', 'project'].includes(options.scope))
275
+ throw new Error('OMP MCP scope must be user or project');
276
+ if ((provider === 'omp' || provider === 'oh-my-pi') && options.scope === 'user')
277
+ return resolve(resolveOmpPaths().agentDir, 'mcp.json');
262
278
  const homeDir = process.env.HOME || process.env.USERPROFILE || '';
263
279
  const pathMap = {
280
+ antigravity: resolve(projectDir, '.agents/mcp_config.json'),
281
+ agy: resolve(projectDir, '.agents/mcp_config.json'),
282
+ omp: resolve(projectDir, '.omp/mcp.json'),
283
+ 'oh-my-pi': resolve(projectDir, '.omp/mcp.json'),
264
284
  'claude-code': resolve(projectDir, '.claude/settings.local.json'),
265
285
  claude: resolve(projectDir, '.claude/settings.local.json'),
266
286
  cursor: resolve(projectDir, '.cursor/mcp.json'),
@@ -281,7 +301,7 @@ export function getProviderConfigPath(provider, projectDir = '.') {
281
301
  */
282
302
  export async function injectServers(registry, provider, options = {}) {
283
303
  const { servers: serverFilter, projectDir = '.', dryRun = false } = options;
284
- const configPath = getProviderConfigPath(provider, projectDir);
304
+ const configPath = getProviderConfigPath(provider, projectDir, options);
285
305
  const result = {
286
306
  provider,
287
307
  configPath,
@@ -297,6 +317,18 @@ export async function injectServers(registry, provider, options = {}) {
297
317
  result.error = 'No servers to inject. Use "aiwg mcp add" first.';
298
318
  return result;
299
319
  }
320
+ if (provider === 'omp' || provider === 'oh-my-pi') {
321
+ try {
322
+ const managed = await manageOmpMcp(configPath, allServers, { dryRun });
323
+ if (!dryRun)
324
+ for (const server of allServers)
325
+ await registry.recordInjection(server.name, 'omp');
326
+ return { ...result, ...managed };
327
+ }
328
+ catch (error) {
329
+ return { ...result, error: error instanceof Error ? error.message : String(error) };
330
+ }
331
+ }
300
332
  // Handle TOML-based providers (Codex/OpenAI) separately
301
333
  if (provider === 'codex' || provider === 'openai') {
302
334
  return injectToml(registry, allServers, configPath, provider, dryRun, result);
@@ -311,8 +343,10 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
311
343
  const content = await readFile(configPath, 'utf-8');
312
344
  existing = JSON.parse(content);
313
345
  }
314
- catch {
315
- // File doesn't exist, start fresh
346
+ catch (error) {
347
+ if ((provider === 'antigravity' || provider === 'agy') && error?.code !== 'ENOENT') {
348
+ throw new Error(`Refusing to overwrite malformed MCP config ${configPath}: ${error.message}`);
349
+ }
316
350
  }
317
351
  // Determine the MCP servers key for this provider
318
352
  const mcpKey = provider === 'opencode' ? 'mcp' : 'mcpServers';
@@ -323,6 +357,8 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
323
357
  if (existingServers[server.name]) {
324
358
  // Update existing entry in place
325
359
  result.alreadyPresent.push(server.name);
360
+ if (provider === 'antigravity' || provider === 'agy')
361
+ continue;
326
362
  }
327
363
  newServers[server.name] = buildServerConfig(server, provider);
328
364
  result.serversInjected.push(server.name);
@@ -334,7 +370,9 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
334
370
  await writeFile(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
335
371
  // Record injection in registry
336
372
  for (const server of servers) {
337
- await registry.recordInjection(server.name, provider);
373
+ if ((provider === 'antigravity' || provider === 'agy') && !result.serversInjected.includes(server.name))
374
+ continue;
375
+ await registry.recordInjection(server.name, provider === 'agy' ? 'antigravity' : provider);
338
376
  }
339
377
  }
340
378
  return result;
@@ -378,6 +416,8 @@ function escapeRegex(str) {
378
416
  }
379
417
  /** All supported provider names for injection */
380
418
  export const SUPPORTED_PROVIDERS = [
419
+ 'antigravity',
420
+ 'omp',
381
421
  'claude-code',
382
422
  'cursor',
383
423
  'factory',
@@ -1,3 +1,5 @@
1
+ import { manageOmpMcp } from './omp-config.mjs';
2
+ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
1
3
  /**
2
4
  * MCP Server Registry (Runtime ESM)
3
5
  *
@@ -206,6 +208,12 @@ function buildServerConfig(server, provider) {
206
208
  const mcpDefinition = getMcpInjectionDefinition(provider);
207
209
 
208
210
  switch (mcpDefinition?.serverConfigFormat) {
211
+ case 'antigravity': {
212
+ if (server.type === 'stdio') {
213
+ return { command: server.command, args: server.args || [], ...(server.env ? { env: server.env } : {}) };
214
+ }
215
+ return { serverUrl: server.url, ...(server.headers ? { headers: server.headers } : {}) };
216
+ }
209
217
  case 'standard': {
210
218
  if (server.type === 'stdio') {
211
219
  return {
@@ -281,15 +289,16 @@ function buildServerToml(server) {
281
289
  return lines.join('\n');
282
290
  }
283
291
 
284
- export function getProviderConfigPath(provider, projectDir = '.') {
285
- return resolveMcpConfigPath(provider, projectDir);
292
+ export function getProviderConfigPath(provider, projectDir = '.', options = {}) {
293
+ if (normalizeRuntimeProviderId(provider) === 'omp' && options.scope !== undefined && !['user', 'project'].includes(options.scope)) throw new Error('OMP MCP scope must be user or project');
294
+ return resolveMcpConfigPath(provider, projectDir, options);
286
295
  }
287
296
 
288
297
  export async function injectServers(registry, provider, options = {}) {
289
298
  const { servers: serverFilter, projectDir = '.', dryRun = false } = options;
290
299
  const normalizedProvider = normalizeRuntimeProviderId(provider);
291
300
  const mcpDefinition = getMcpInjectionDefinition(provider);
292
- const configPath = getProviderConfigPath(provider, projectDir);
301
+ const configPath = getProviderConfigPath(provider, projectDir, options);
293
302
  const result = {
294
303
  provider,
295
304
  configPath,
@@ -312,6 +321,16 @@ export async function injectServers(registry, provider, options = {}) {
312
321
  return result;
313
322
  }
314
323
 
324
+ if (normalizedProvider === 'omp') {
325
+ try {
326
+ const managed = await manageOmpMcp(configPath, allServers, { dryRun });
327
+ if (!dryRun) for (const server of allServers) await registry.recordInjection(server.name, 'omp');
328
+ return { ...result, ...managed };
329
+ } catch (error) {
330
+ return { ...result, error: error instanceof Error ? error.message : String(error) };
331
+ }
332
+ }
333
+
315
334
  if (mcpDefinition?.configFormat === 'toml') {
316
335
  return injectToml(registry, allServers, configPath, provider, dryRun, result);
317
336
  }
@@ -324,8 +343,10 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
324
343
  try {
325
344
  const content = await readFile(configPath, 'utf-8');
326
345
  existing = JSON.parse(content);
327
- } catch {
328
- // File doesn't exist
346
+ } catch (error) {
347
+ if (normalizeRuntimeProviderId(provider) === 'antigravity' && error?.code !== 'ENOENT') {
348
+ throw new Error(`Refusing to overwrite malformed MCP config ${configPath}: ${error.message}`);
349
+ }
329
350
  }
330
351
 
331
352
  const mcpKey = getMcpInjectionDefinition(provider)?.serversKey || 'mcpServers';
@@ -335,6 +356,7 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
335
356
  for (const server of servers) {
336
357
  if (existingServers[server.name]) {
337
358
  result.alreadyPresent.push(server.name);
359
+ if (normalizeRuntimeProviderId(provider) === 'antigravity') continue;
338
360
  }
339
361
  newServers[server.name] = buildServerConfig(server, provider);
340
362
  result.serversInjected.push(server.name);
@@ -347,7 +369,8 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
347
369
  await writeFile(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
348
370
 
349
371
  for (const server of servers) {
350
- await registry.recordInjection(server.name, provider);
372
+ if (normalizeRuntimeProviderId(provider) === 'antigravity' && !result.serversInjected.includes(server.name)) continue;
373
+ await registry.recordInjection(server.name, normalizeRuntimeProviderId(provider) || provider);
351
374
  }
352
375
  }
353
376
 
@@ -3,6 +3,17 @@
3
3
  "version": "1.0.0",
4
4
  "verifiedAt": "2026-07-20",
5
5
  "providers": {
6
+ "antigravity": {
7
+ "agent": "native", "skill": "native", "globalChild": "inherited",
8
+ "identifierSyntax": "model identifier accepted by Antigravity CLI 1.1.26 --model",
9
+ "effortValues": ["low", "medium", "high"],
10
+ "inheritance": "Omitted model and effort inherit the invoking Antigravity session",
11
+ "invalidPinFallback": "Provider validation/error; never assume fallback",
12
+ "configTarget": "agy launch arguments; custom-agent model tiers are not projected from generic AIWG model ids",
13
+ "artifactFormat": "YAML frontmatter plus Markdown; runtime --model and --effort flags",
14
+ "verification": "Pinned offline contract; authenticated model selection was not executed",
15
+ "sourceUrl": "https://antigravity.google/docs/cli/subagents/", "verifiedAt": "2026-09-04"
16
+ },
6
17
  "claude": {
7
18
  "agent": "native", "skill": "native", "globalChild": "inherited",
8
19
  "identifierSyntax": "Claude alias or accepted Anthropic model ID",
@@ -76,6 +87,26 @@
76
87
  "verification": "Inspect active profile and run metadata",
77
88
  "sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions", "verifiedAt": "2026-07-20"
78
89
  },
90
+ "omp": {
91
+ "agent": "native",
92
+ "skill": "inherited",
93
+ "globalChild": "native",
94
+ "identifierSyntax": "provider/model identifier accepted by OMP --model",
95
+ "effortValues": [
96
+ "minimal",
97
+ "low",
98
+ "medium",
99
+ "high",
100
+ "xhigh"
101
+ ],
102
+ "inheritance": "Omitted model and thinking level inherit the invoking Pi session",
103
+ "invalidPinFallback": "Pi resolves against its configured catalog and exits on invalid explicit selection",
104
+ "configTarget": "OMP native agent frontmatter and runtime launch arguments",
105
+ "artifactFormat": "model and thinkingLevel frontmatter; --model and --thinking runtime flags",
106
+ "verification": "Pinned OMP 18.1.10 source and live OpenRouter smoke; individual role defaults remain operator configured",
107
+ "sourceUrl": "https://github.com/can1357/oh-my-pi/tree/5964a0f7649275bcde818f20073193fd032451f2/packages/coding-agent",
108
+ "verifiedAt": "2026-09-04"
109
+ },
79
110
  "pi": {
80
111
  "agent": "native", "skill": "inherited", "globalChild": "native",
81
112
  "identifierSyntax": "provider/model identifier accepted by Pi --model",
@@ -4,6 +4,14 @@
4
4
  "refreshedAt": "2026-07-20",
5
5
  "staleAfterDays": 90,
6
6
  "providers": {
7
+ "antigravity": {
8
+ "roles": {
9
+ "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
10
+ "coding": { "id": "configured/coding", "status": "unverified", "observed": false },
11
+ "efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
12
+ },
13
+ "sourceUrl": "https://antigravity.google/docs/cli/overview/", "verifiedAt": "2026-09-04"
14
+ },
7
15
  "claude": {
8
16
  "roles": {
9
17
  "reasoning": { "id": "claude-opus-4-7", "status": "active", "observed": false },
@@ -52,6 +60,27 @@
52
60
  },
53
61
  "sourceUrl": "https://opencode.ai/docs/models", "verifiedAt": "2026-07-20"
54
62
  },
63
+ "omp": {
64
+ "roles": {
65
+ "reasoning": {
66
+ "id": "configured/reasoning",
67
+ "status": "unverified",
68
+ "observed": false
69
+ },
70
+ "coding": {
71
+ "id": "configured/coding",
72
+ "status": "unverified",
73
+ "observed": false
74
+ },
75
+ "efficiency": {
76
+ "id": "configured/efficiency",
77
+ "status": "unverified",
78
+ "observed": false
79
+ }
80
+ },
81
+ "sourceUrl": "https://github.com/can1357/oh-my-pi/tree/5964a0f7649275bcde818f20073193fd032451f2/packages/coding-agent",
82
+ "verifiedAt": "2026-09-04"
83
+ },
55
84
  "pi": {
56
85
  "roles": {
57
86
  "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
@@ -67,6 +67,9 @@ export const PROVIDER_DISCOVERY_DECISIONS = {
67
67
  reason: 'OpenHuman profiles accept semantic model hints but expose no standardized local model-list command.',
68
68
  documentation: 'https://github.com/roctinam/openhuman',
69
69
  },
70
+ omp: { provider: 'omp', status: 'native', interface: 'omp models --json --no-extensions',
71
+ reason: 'OMP JSON catalog reports credential-available LLM models; ambient extensions are disabled by default.',
72
+ documentation: 'https://github.com/can1357/oh-my-pi/blob/5964a0f7649275bcde818f20073193fd032451f2/packages/coding-agent/src/commands/models.ts' },
70
73
  pi: {
71
74
  provider: 'pi',
72
75
  status: 'native',
@@ -103,7 +106,7 @@ export function classifyDiscoveryError(message) {
103
106
  export const runModelDiscoveryCommand = (command, args, options = {}) => new Promise(resolveCommand => {
104
107
  const child = spawn(command, args, {
105
108
  cwd: options.cwd,
106
- env: process.env,
109
+ env: options.env ?? process.env,
107
110
  stdio: ['ignore', 'pipe', 'pipe'],
108
111
  });
109
112
  let stdout = '';
@@ -116,8 +119,13 @@ export const runModelDiscoveryCommand = (command, args, options = {}) => new Pro
116
119
  clearTimeout(timer);
117
120
  resolveCommand({ stdout, stderr, exitCode });
118
121
  };
119
- child.stdout.on('data', chunk => { stdout += String(chunk); });
120
- child.stderr.on('data', chunk => { stderr += String(chunk); });
122
+ child.stdout.on('data', chunk => { stdout += String(chunk); if (Buffer.byteLength(stdout) > 8 * 1024 * 1024) {
123
+ stdout = '';
124
+ stderr = 'Model catalog exceeded 8 MiB limit';
125
+ child.kill('SIGKILL');
126
+ finish(1);
127
+ } });
128
+ child.stderr.on('data', chunk => { stderr = (stderr + String(chunk)).slice(-4096); });
121
129
  child.on('error', error => {
122
130
  stderr = `${stderr}${stderr ? '\n' : ''}${error.message}`;
123
131
  finish(127);
@@ -425,7 +433,7 @@ export async function resolveDynamicModelCatalog(options) {
425
433
  const homeDir = options.homeDir ?? homedir();
426
434
  const cacheFile = options.cacheFile ?? join(homeDir, '.cache/aiwg/model-catalog.v1.json');
427
435
  const staticFile = join(options.aiwgRoot, 'agentic/code/providers/model-catalog.v1.json');
428
- const signature = inventorySignature(options.inventory);
436
+ const signature = inventorySignature(options.inventory) + JSON.stringify({ omp: options.omp, profile: process.env.OMP_PROFILE ?? process.env.PI_PROFILE, config: process.env.PI_CONFIG_DIR, data: process.env.XDG_DATA_HOME, cache: process.env.XDG_CACHE_HOME, agent: process.env.PI_CODING_AGENT_DIR });
429
437
  const staticCatalog = await readCatalog(staticFile);
430
438
  if (!staticCatalog)
431
439
  throw new Error(`Invalid or missing static model catalog: ${staticFile}`);
@@ -487,6 +495,7 @@ export async function resolveDynamicModelCatalog(options) {
487
495
  opencode: () => discoverOpenCodeModels(),
488
496
  openclaw: () => discoverOpenClawModels(),
489
497
  pi: () => discoverPiModels(),
498
+ omp: () => discoverOmpModels(process.env.AIWG_OMP_BIN || 'omp', runModelDiscoveryCommand, options.omp),
490
499
  };
491
500
  const providerDiscovery = {};
492
501
  for (const provider of available) {
@@ -494,7 +503,7 @@ export async function resolveDynamicModelCatalog(options) {
494
503
  if (discoverer) {
495
504
  try {
496
505
  providerDiscovery[provider] = await discoverer();
497
- const selected = selectRoleModels(providerDiscovery[provider].models);
506
+ const selected = provider === 'omp' ? {} : selectRoleModels(providerDiscovery[provider].models);
498
507
  const providerCatalog = catalog.providers[provider];
499
508
  if (providerCatalog) {
500
509
  for (const role of ['reasoning', 'coding', 'efficiency']) {
@@ -550,4 +559,36 @@ export async function resolveDynamicModelCatalog(options) {
550
559
  await atomicWrite(cacheFile, `${JSON.stringify(resolved, null, 2)}\n`);
551
560
  return resolved;
552
561
  }
562
+ /** Explicit OMP discovery; no ambient extension execution unless requested. */
563
+ export async function discoverOmpModels(command = 'omp', runner = runModelDiscoveryCommand, options = {}) {
564
+ const base = { provider: 'omp', source: 'native', observedAt: new Date().toISOString(), accountScope: 'local-runtime', models: [] };
565
+ const args = [...(options.profile ? ['--profile', options.profile] : []), 'models', '--json'];
566
+ if (!options.extensions)
567
+ args.push('--no-extensions');
568
+ for (const config of options.config ?? [])
569
+ args.push('--config', config);
570
+ try {
571
+ const result = await runner(command, args, { cwd: options.cwd ?? tmpdir(), timeoutMs: 15000 });
572
+ base.runtimeVersion = await runtimeVersion(command, runner);
573
+ if (result.exitCode !== 0)
574
+ return { ...base, errorKind: result.exitCode === 124 ? 'timeout' : /unknown|unsupported|not found/i.test(result.stderr) ? 'unsupported' : classifyDiscoveryError(result.stderr), error: 'OMP model discovery failed; check CLI version, selected profile/config, and credential availability.' };
575
+ const payload = JSON.parse(result.stdout);
576
+ if (!Array.isArray(payload.models) || payload.models.some((m) => !m || typeof m.id !== 'string' || !m.id || typeof m.provider !== 'string' || !m.provider))
577
+ throw new Error('shape');
578
+ base.models = payload.models.map((m) => ({ id: `${m.provider}/${m.id}`, llmProvider: m.provider, displayName: m.name, reasoningEfforts: Array.isArray(m.thinking) ? m.thinking : undefined }));
579
+ return base;
580
+ }
581
+ catch {
582
+ return { ...base, errorKind: 'invalid-output', error: 'OMP returned invalid model catalog JSON or discovery could not run.' };
583
+ }
584
+ }
585
+ /** Never guess an unrelated model when a requested mapping is unavailable. */
586
+ export function resolveOmpRoleModel(catalog, role, mappings, explicit) {
587
+ if (explicit)
588
+ return explicit;
589
+ const id = mappings[role];
590
+ if (!id || !catalog.models.some(model => model.id === id))
591
+ throw new Error(`OMP ${role} model unavailable; configure a model from omp models --json or supply an explicit model.`);
592
+ return id;
593
+ }
553
594
  //# sourceMappingURL=model-discovery.js.map
@@ -26,8 +26,8 @@ function requireModelResource(filename) {
26
26
  const capabilityData = requireModelResource('model-capabilities.v1.json');
27
27
  const catalogData = requireModelResource('model-catalog.v1.json');
28
28
  const ProviderSchema = z.enum([
29
- 'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
30
- 'opencode', 'openclaw', 'openhuman', 'pi', 'warp', 'windsurf',
29
+ 'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
30
+ 'opencode', 'openclaw', 'openhuman', 'omp', 'pi', 'warp', 'windsurf',
31
31
  ]);
32
32
  const OutcomeSchema = z.enum([
33
33
  'native', 'compiled', 'inherited', 'global-only', 'informational', 'unsupported',
@@ -107,7 +107,7 @@ export function loadProviderModelCapabilities() {
107
107
  const expected = new Set(ProviderSchema.options);
108
108
  const actual = new Set(Object.keys(registryCache.providers));
109
109
  if (actual.size !== expected.size || [...expected].some(id => !actual.has(id))) {
110
- throw new Error('Provider model capability registry must cover all 12 providers');
110
+ throw new Error('Provider model capability registry must cover all registered providers');
111
111
  }
112
112
  }
113
113
  return registryCache;
@@ -153,12 +153,14 @@ function catalogRole(policy) {
153
153
  }
154
154
  function fieldNames(provider) {
155
155
  switch (provider) {
156
+ case 'antigravity': return { model: 'model', effort: 'effort' };
156
157
  case 'codex': return { model: 'model', effort: 'model_reasoning_effort' };
157
158
  case 'factory': return { model: 'model', effort: 'reasoningEffort' };
158
159
  case 'claude': return { model: 'model', effort: 'effort' };
159
160
  case 'copilot':
160
161
  case 'cursor':
161
162
  case 'opencode': return { model: 'model' };
163
+ case 'omp': return { model: 'model', effort: 'thinkingLevel' };
162
164
  case 'pi': return { model: 'model', effort: 'thinking' };
163
165
  case 'openhuman': return { model: 'model_hint' };
164
166
  case 'openclaw': return { model: 'subagents.model' };
@@ -34,11 +34,13 @@ const PROVIDERS_NEEDING_COMMANDS = new Set([
34
34
  * Providers where skills are deployed natively (no command generation needed).
35
35
  */
36
36
  const SKILLS_ONLY_PROVIDERS = new Set([
37
+ 'antigravity',
37
38
  'claude',
38
39
  'cursor',
39
40
  'hermes',
40
41
  'openhuman',
41
42
  'pi',
43
+ 'omp',
42
44
  ]);
43
45
  /**
44
46
  * Check if a provider needs command files generated from skills.
@@ -361,7 +361,7 @@ providers:
361
361
 
362
362
  hermes:
363
363
  display_name: Hermes
364
- status: experimental
364
+ status: stable
365
365
  daemon_tier: unsupported
366
366
  daemon_pty_adapter: false
367
367
  artifact_paths:
@@ -470,6 +470,51 @@ providers:
470
470
  deploy_target: mixed
471
471
  aggregated_output: true
472
472
 
473
+ antigravity:
474
+ display_name: Google Antigravity CLI
475
+ aliases:
476
+ - agy
477
+ status: experimental
478
+ executable: agy
479
+ qualified_version: 1.1.26
480
+ qualification_evidence: agentic/code/providers/antigravity/provider-contract.v1.json
481
+ daemon_tier: unsupported
482
+ daemon_pty_adapter: false
483
+ artifact_paths:
484
+ agents: .agents/agents/
485
+ commands: "(not deployed; no qualified native command surface)"
486
+ skills: .agents/skills/
487
+ rules: AGENTS.md
488
+ behaviors: "(not supported)"
489
+ mcp_project: .agents/mcp_config.json
490
+ mcp_user: "~/.gemini/config/mcp_config.json"
491
+ native_features:
492
+ cron: false
493
+ agent_teams: false
494
+ tasks: false
495
+ mcp: true
496
+ behaviors: false
497
+ mission_control: false
498
+ daemon: false
499
+ emulation:
500
+ cron: external-trigger
501
+ agent_teams: aiwg-mc
502
+ tasks: aiwg-mc
503
+ mcp: native
504
+ behaviors: null
505
+ mission_control: aiwg-mc
506
+ daemon: null
507
+ hook_wiring:
508
+ at_link_support: false
509
+ fallback: full-inject
510
+ context_file: AGENTS.md
511
+ interaction:
512
+ structured_questions: none
513
+ fallback: markdown
514
+ notes: No stable structured-question tool contract was qualified for CLI 1.1.26.
515
+ deploy_target: project
516
+ aggregated_output: false
517
+
473
518
  pi:
474
519
  display_name: Pi Coding Agent
475
520
  aliases:
@@ -511,6 +556,47 @@ providers:
511
556
  deploy_target: project
512
557
  aggregated_output: false
513
558
 
559
+ omp:
560
+ display_name: Oh My Pi
561
+ aliases:
562
+ - oh-my-pi
563
+ status: experimental
564
+ daemon_tier: unsupported
565
+ daemon_pty_adapter: false
566
+ artifact_paths:
567
+ agents: .omp/agents/
568
+ commands: .omp/prompts/
569
+ skills: .agents/skills/
570
+ skills_cross_agent: .agents/skills/
571
+ rules: .omp/rules/
572
+ behaviors: .omp/extensions/
573
+ native_features:
574
+ cron: false
575
+ agent_teams: true
576
+ tasks: true
577
+ mcp: true
578
+ behaviors: true
579
+ mission_control: false
580
+ daemon: false
581
+ emulation:
582
+ cron: external-trigger
583
+ agent_teams: null
584
+ tasks: null
585
+ mcp: null
586
+ behaviors: null
587
+ mission_control: aiwg-mc
588
+ daemon: null
589
+ hook_wiring:
590
+ at_link_support: true
591
+ fallback: full-inject
592
+ context_file: .omp/AGENTS.md
593
+ interaction:
594
+ structured_questions: none
595
+ fallback: markdown
596
+ notes: OMP integration uses Markdown questions; native UI questions require an interactive runtime.
597
+ deploy_target: project
598
+ aggregated_output: false
599
+
514
600
  # Feature definitions — documents what each feature key means and
515
601
  # what emulation strategies are available when native support is absent.
516
602
  features:
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+ import YAML from 'yaml';
3
+ function parse(content) {
4
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
5
+ return match ? { metadata: YAML.parse(match[1]) || {}, body: match[2] } : { metadata: {}, body: content };
6
+ }
7
+ const render = (metadata, body) => `---\n${YAML.stringify(metadata)}---\n\n${body.trim()}\n`;
8
+ function diagnostic(opts, message) { opts.diagnostics?.push(message); if (!opts.quiet) console.warn(`OMP: ${message}`); }
9
+ export function mapModel(model, config = {}) {
10
+ if (!model || model === 'inherit') return undefined;
11
+ const mapped = config.omp?.[model]?.model || config.omp?.[model];
12
+ if (typeof mapped === 'string') return mapped;
13
+ if (['opus', 'sonnet', 'haiku', 'reasoning', 'coding', 'efficiency', 'heavy', 'medium', 'light'].includes(model)) return undefined;
14
+ return model;
15
+ }
16
+ const toolMap = { Read: 'read', Write: 'write', Edit: 'edit', MultiEdit: 'edit', Bash: 'bash', Grep: 'grep', Glob: 'glob', WebSearch: 'web_search', Task: 'task', TodoWrite: 'todo' };
17
+ const nativeTools = new Set(['read','security_scan','bash','edit','ast_grep','ast_edit','ask','debug','eval','github','glob','grep','lsp','checkpoint','rewind','task','hub','todo','web_search','write','memory_edit','retain','recall','reflect','learn','manage_skill','think','yield','goal']);
18
+ const array = value => Array.isArray(value) ? value : typeof value === 'string' ? value.split(',').map(s => s.trim()).filter(Boolean) : [];
19
+ export function transformAgent(src, content, opts = {}) {
20
+ const { metadata: m, body } = parse(content);
21
+ const agentName = m.name ?? path.basename(src, '.md');
22
+ if (typeof agentName !== 'string' || !agentName.trim()) throw new Error('OMP agent name must be a nonempty string');
23
+ const out = { name: agentName.trim(), description: String(m.description || `AIWG ${path.basename(src, '.md')} agent`) };
24
+ if (['main','sub'].includes(out.name.toLowerCase())) throw new Error(`OMP reserves agent name ${out.name}`);
25
+ const tools = array(m.tools).flatMap(tool => {
26
+ const mapped = toolMap[tool] || (nativeTools.has(tool) ? tool : undefined);
27
+ if (!mapped) diagnostic(opts, `${out.name}: unsupported tool ${tool}; omitted (degraded)`);
28
+ return mapped ? [mapped] : [];
29
+ });
30
+ // OMP treats empty spawns as undefined; remove task unless explicit delegation is configured.
31
+ out.spawns = m.spawns === '*' ? '*' : array(m.spawns);
32
+ out.tools = [...new Set(tools)].filter(tool => tool !== 'task' || out.spawns === '*' || out.spawns.length > 0);
33
+ if (m.model) {
34
+ const models = array(m.model).flatMap(model => { const mapped = mapModel(model, opts.modelsConfig || opts.modelConfig || {}); if (!mapped && model !== 'inherit') diagnostic(opts, `${out.name}: unmapped model role ${model}; inherits session model (degraded)`); return mapped ? [mapped] : []; });
35
+ if (models.length) out.model = models.length === 1 ? models[0] : models;
36
+ }
37
+ for (const key of ['thinkingLevel', 'blocking', 'output', 'autoloadSkills', 'readSummarize', 'prewalk', 'advisor']) if (m[key] !== undefined) out[key] = m[key];
38
+ if (!out.thinkingLevel && m.thinking) out.thinkingLevel = m.thinking;
39
+ return render(out, body);
40
+ }
@@ -0,0 +1,15 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { resolveOmpPaths } from './omp-paths.mjs';
4
+ const execute = promisify(execFile);
5
+ /** Reports only parsed version and non-secret paths; never reflects runtime output. */
6
+ export async function diagnoseOmpRuntime({ cwd = process.cwd(), env = process.env, binary = env.AIWG_OMP_BIN || 'omp', runner = execute } = {}) {
7
+ const paths = resolveOmpPaths({ cwd, env });
8
+ const result = { executable: binary, available: false, version: null, paths, reason: 'OMP executable unavailable; install the supported binary or set AIWG_OMP_BIN.' };
9
+ try {
10
+ const output = await runner(binary, ['--version'], { cwd, env: { PATH: env.PATH || '', PI_CODING_AGENT_DIR: paths.agentDir, NO_COLOR: '1', ...(env.SystemRoot ? { SystemRoot: env.SystemRoot } : {}) }, timeout: 5000, maxBuffer: 16384, encoding: 'utf8' });
11
+ const version = output.stdout.trim().match(/^(?:omp\/)?(\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9.-]+)?)$/)?.[1];
12
+ if (!version) { result.reason = 'OMP returned an unrecognized version; verify the selected executable.'; return result; }
13
+ return { ...result, available: true, version, reason: null };
14
+ } catch { return result; }
15
+ }
@@ -0,0 +1,38 @@
1
+ import { homedir } from 'node:os';
2
+ import path from 'node:path';
3
+ import { existsSync } from 'node:fs';
4
+
5
+ /** OMP 18.1.10 profile contract; independent of original Pi configuration. */
6
+ export function normalizeOmpProfile(value) {
7
+ const name = value?.trim();
8
+ if (!name || name === 'default') return undefined;
9
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(name) || name.endsWith('.')
10
+ || /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\..*)?$/i.test(name)) {
11
+ throw new Error('Invalid OMP profile: use 1–64 lowercase letters, digits, dots, hyphens or underscores; reserved device names are forbidden');
12
+ }
13
+ return name;
14
+ }
15
+
16
+ /** Resolve without reading credentials or importing the OMP runtime. */
17
+ export function resolveOmpPaths({ env = process.env, home = homedir(), cwd = process.cwd(),
18
+ platform = process.platform, exists = existsSync } = {}) {
19
+ const profile = normalizeOmpProfile(env.OMP_PROFILE !== undefined ? env.OMP_PROFILE : env.PI_PROFILE);
20
+ const directory = env.PI_CONFIG_DIR || '.omp';
21
+ const base = path.join(home, directory);
22
+ const configRoot = profile ? path.join(base, 'profiles', profile) : base;
23
+ const defaultAgent = path.join(configRoot, 'agent');
24
+ const agentDir = !profile && env.PI_CODING_AGENT_DIR
25
+ ? path.resolve(cwd, env.PI_CODING_AGENT_DIR) : defaultAgent;
26
+ const xdg = (variable) => {
27
+ if (!['linux', 'darwin'].includes(platform) || agentDir !== defaultAgent || !env[variable]) return undefined;
28
+ const root = path.join(env[variable], 'omp');
29
+ const candidate = profile ? path.join(root, 'profiles', profile) : root;
30
+ return exists(candidate) ? candidate : undefined;
31
+ };
32
+ const dataDir = xdg('XDG_DATA_HOME') || agentDir;
33
+ const stateDir = xdg('XDG_STATE_HOME') || agentDir;
34
+ const cacheDir = xdg('XDG_CACHE_HOME') || agentDir;
35
+ const resourceDirs = { agents: path.join(defaultAgent, 'agents'), skills: path.join(agentDir, 'skills'), commands: path.join(agentDir, 'prompts'), rules: path.join(agentDir, 'rules'), behaviors: path.join(agentDir, 'extensions') };
36
+ return { profile, configRoot, agentDir, resourceDirs, projectDir: path.join(cwd, '.omp'),
37
+ dataDir, stateDir, cacheDir, sessionsDir: path.join(dataDir, 'sessions') };
38
+ }