omniagent 0.1.12 → 0.1.14

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # omniagent
2
2
 
3
- One source of truth for agent config across Claude, Codex, Gemini, and Copilot (and [any other agent](docs/custom-targets.md))
3
+ One source of truth for agent config across Claude, Codex, Antigravity (agy), and Copilot (and [any other agent](docs/custom-targets.md))
4
4
 
5
5
  Define canonical agent files once in `agents/`, then run `sync` to compile target-specific outputs.
6
6
 
@@ -89,14 +89,15 @@ omniagent --agent codex
89
89
  omniagent -p "Summarize this repo" --agent codex --output json
90
90
  ```
91
91
 
92
- `usage` supports Codex, Claude, and Gemini in v1. Copilot is not supported for usage
93
- extraction yet. Usage extraction may launch agent TUIs and may incur cost if an agent
94
- reads repo context or instructions on startup; omniagent uses cheap/minimal launch settings
95
- where possible. Usage extraction times out after 30 seconds unless the target config defines
96
- a target-specific timeout; built-in TUI probes may use longer defaults. Some CLIs require
97
- session-scoped setup flags to inspect usage, such as Gemini's `--skip-trust`; omniagent does
98
- not complete auth or onboarding prompts for you. Pass `--timeout=<seconds>` to override the
99
- per-agent timeout for the current run.
92
+ `usage` supports Codex, Claude, and Antigravity (`agy`; `gemini` works as an alias). Copilot
93
+ is not supported for usage extraction yet. Usage extraction may launch agent TUIs and may incur
94
+ cost if an agent reads repo context or instructions on startup; omniagent uses cheap/minimal
95
+ launch settings where possible. Usage extraction times out after 30 seconds unless the target
96
+ config defines a target-specific timeout; built-in TUI probes may use longer defaults. Some CLIs
97
+ gate usage inspection behind onboarding state Antigravity requires the project to be trusted
98
+ (run `agy` once and accept the trust prompt); omniagent does not complete auth or onboarding
99
+ prompts for you. Pass `--timeout=<seconds>` to override the per-agent timeout for the current
100
+ run.
100
101
 
101
102
  ## Local Overrides (`.local`)
102
103
 
@@ -213,6 +214,18 @@ Example usage:
213
214
  ./code-review.sh codex
214
215
  ```
215
216
 
217
+ The shim also unifies structured outputs: pass a JSON schema (file path or inline JSON) with
218
+ `--output-schema`, and stdout is exactly the schema-conforming JSON regardless of agent. Agents
219
+ with native schema support (claude, codex) enforce it server-side; all others get a prompt-based
220
+ fallback with client-side validation and automatic retries:
221
+
222
+ ```bash
223
+ omniagent -p "Top 3 benefits of TypeScript" --agent claude \
224
+ --output-schema ./schema.json | jq .answer
225
+ ```
226
+
227
+ See [`docs/cli-shim.md`](docs/cli-shim.md) for the full shared-flag capability matrix.
228
+
216
229
  ## Documentation
217
230
 
218
231
  - Docs index: [`docs/README.md`](docs/README.md)
@@ -0,0 +1,138 @@
1
+ import { c as cleanControlOutput, m as makeUsageLimit, a as compactLines } from "./cli.js";
2
+ import { r as runPtyScenario, t as typeTextSteps, e as escapeKey, a as enterKey } from "./pty-CMd3Wxfe.js";
3
+ const TRUST_DIALOG_PATTERN = /Do you trust the contents of this project\?/i;
4
+ const READY_PATTERN = /\?\s+for shortcuts/i;
5
+ const REMAINING_CREDITS_PATTERN = /Remaining AI Credits:\s*(.+?)\s*$/i;
6
+ const CREDITS_NOT_ENABLED_PATTERN = /AI Credits not enabled/i;
7
+ const ACCOUNT_PLAN_PATTERN = /^\S+@\S+\.\S+\s+\((.+?)\)\s*$/;
8
+ const STANDALONE_PLAN_PATTERN = /^\((.*(?:quota|plan|tier).*)\)$/i;
9
+ const SIGN_IN_PATTERN = /\b(?:not signed in|Signing in)\b/i;
10
+ function isTrustDialog(snapshot) {
11
+ return TRUST_DIALOG_PATTERN.test(snapshot.raw) || TRUST_DIALOG_PATTERN.test(snapshot.screen);
12
+ }
13
+ function isReadyOrTrustDialog(snapshot) {
14
+ return READY_PATTERN.test(snapshot.screen) || isTrustDialog(snapshot);
15
+ }
16
+ function hasCreditsPanel(snapshot) {
17
+ return REMAINING_CREDITS_PATTERN.test(snapshot.screen) || REMAINING_CREDITS_PATTERN.test(cleanControlOutput(snapshot.raw));
18
+ }
19
+ function withTrustSkip(step) {
20
+ return { ...step, skipIf: isTrustDialog, skipIfSource: "raw" };
21
+ }
22
+ async function extractAgyUsage(context) {
23
+ const command = context.command ?? context.launch?.command ?? "agy";
24
+ const ptyResult = await runPtyScenario({
25
+ command,
26
+ args: context.launch?.args ?? [],
27
+ cwd: context.repoRoot,
28
+ cols: 120,
29
+ rows: 40,
30
+ timeoutMs: context.launch?.timeoutMs ?? 7e4,
31
+ signal: context.signal,
32
+ debug: context.debug,
33
+ steps: [
34
+ { waitFor: isReadyOrTrustDialog, waitForTimeoutMs: 25e3 },
35
+ ...typeTextSteps("/credits", 25).map(withTrustSkip),
36
+ withTrustSkip({ waitMs: 250, write: enterKey() }),
37
+ withTrustSkip({
38
+ waitFor: hasCreditsPanel,
39
+ waitForTimeoutMs: 15e3,
40
+ optional: true,
41
+ capture: "credits",
42
+ captureWaitMs: 500
43
+ }),
44
+ { write: escapeKey(), waitMs: 250 }
45
+ ]
46
+ });
47
+ const buildError = (message) => {
48
+ const error = new Error(message);
49
+ if (ptyResult.debug.length > 0) {
50
+ Object.assign(error, { debug: ptyResult.debug });
51
+ }
52
+ return error;
53
+ };
54
+ if (isTrustDialog(ptyResult)) {
55
+ throw buildError(
56
+ `Antigravity has not trusted this project yet. Run \`${command}\` in ${context.repoRoot} once, accept the trust prompt, then re-run usage.`
57
+ );
58
+ }
59
+ const snapshot = ptyResult.snapshots.credits ?? ptyResult;
60
+ const cleanedOutput = cleanControlOutput(snapshot.raw);
61
+ const parsed = parseAgyCredits(snapshot.screen, cleanedOutput);
62
+ if (parsed.notEnabled) {
63
+ throw buildError(
64
+ "Antigravity AI Credits are not enabled for this account. Enable them via /settings in agy."
65
+ );
66
+ }
67
+ if (parsed.remaining == null) {
68
+ if (SIGN_IN_PATTERN.test(snapshot.screen) || SIGN_IN_PATTERN.test(cleanedOutput)) {
69
+ throw buildError(`Antigravity is not signed in. Run \`${command}\` and complete the login.`);
70
+ }
71
+ throw buildError("Antigravity /credits output did not include a Remaining AI Credits value.");
72
+ }
73
+ return {
74
+ targetId: context.targetId,
75
+ displayName: context.displayName,
76
+ command,
77
+ limits: [
78
+ // Credits are an absolute balance rather than a percentage window.
79
+ makeUsageLimit({
80
+ targetId: context.targetId,
81
+ scope: "ai_credits",
82
+ window: "credits",
83
+ label: parsed.plan ? `AI Credits (${parsed.plan})` : "AI Credits",
84
+ percentUsed: null,
85
+ percentRemaining: null,
86
+ remainingText: parsed.remaining,
87
+ resetText: null,
88
+ raw: parsed.rawLine,
89
+ now: context.now
90
+ })
91
+ ],
92
+ debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
93
+ };
94
+ }
95
+ function parseAgyCredits(screen, cleanedOutput = "") {
96
+ const fromScreen = parseAgyCreditsLines(compactLines(screen));
97
+ if (fromScreen.remaining != null || fromScreen.notEnabled) {
98
+ return fromScreen;
99
+ }
100
+ const fromRaw = parseAgyCreditsLines(compactLines(cleanedOutput));
101
+ if (fromRaw.remaining != null || fromRaw.notEnabled) {
102
+ return fromRaw;
103
+ }
104
+ return fromScreen.plan != null ? fromScreen : fromRaw;
105
+ }
106
+ function parseAgyCreditsLines(lines) {
107
+ const parsed = {
108
+ remaining: null,
109
+ notEnabled: false,
110
+ plan: null,
111
+ rawLine: ""
112
+ };
113
+ for (const line of lines) {
114
+ const remainingMatch = REMAINING_CREDITS_PATTERN.exec(line);
115
+ if (remainingMatch?.[1]) {
116
+ const value = remainingMatch[1].trim();
117
+ if (CREDITS_NOT_ENABLED_PATTERN.test(value)) {
118
+ parsed.notEnabled = true;
119
+ parsed.rawLine = line.trim();
120
+ continue;
121
+ }
122
+ parsed.remaining = value;
123
+ parsed.rawLine = line.trim();
124
+ continue;
125
+ }
126
+ if (parsed.plan == null) {
127
+ const planMatch = ACCOUNT_PLAN_PATTERN.exec(line) ?? STANDALONE_PLAN_PATTERN.exec(line);
128
+ if (planMatch?.[1]) {
129
+ parsed.plan = planMatch[1].trim();
130
+ }
131
+ }
132
+ }
133
+ return parsed;
134
+ }
135
+ export {
136
+ extractAgyUsage,
137
+ parseAgyCredits
138
+ };
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { c as cleanControlOutput, a as compactLines, p as parsePercentUsed, m as makeUsageLimit } from "./cli.js";
7
- import { r as runPtyScenario, e as enterKey, a as escapeKey } from "./pty-CZBSAJzE.js";
7
+ import { r as runPtyScenario, a as enterKey, e as escapeKey } from "./pty-CMd3Wxfe.js";
8
8
  const execFileAsync = promisify(execFile);
9
9
  const CLAUDE_CODE_KEYCHAIN_SERVICE = "Claude Code-credentials";
10
10
  const CLAUDE_CODE_CREDENTIALS_PATH = [".claude", ".credentials.json"];
@@ -50,7 +50,7 @@ async function extractClaudeUsageFromTui(context) {
50
50
  { waitFor: /Claude|>|❯/u, waitForSource: "screen", waitForTimeoutMs: 8e3 },
51
51
  { write: `/usage${enterKey()}` },
52
52
  {
53
- waitFor: hasClaudeUsageRows,
53
+ waitFor: hasClaudeUsageResult,
54
54
  waitForTimeoutMs: 15e3,
55
55
  capture: "usage",
56
56
  captureWaitMs: 500
@@ -65,6 +65,12 @@ async function extractClaudeUsageFromTui(context) {
65
65
  const parsed = parseClaudeUsage(usageSnapshot.screen, cleanedOutput);
66
66
  const limits = buildClaudeUsageLimits(parsed, context);
67
67
  if (limits.length === 0) {
68
+ const usageError = extractClaudeUsageError(usageSnapshot.screen, cleanedOutput);
69
+ if (usageError != null) {
70
+ const error = new Error(`Claude usage error: ${usageError}`);
71
+ Object.assign(error, { debug: ptyResult.debug });
72
+ throw error;
73
+ }
68
74
  throw new Error("Claude usage output did not include session or weekly usage rows.");
69
75
  }
70
76
  return {
@@ -288,6 +294,26 @@ function hasClaudeUsageRows(snapshot) {
288
294
  const parsed = parseClaudeUsage(snapshot.screen, cleanControlOutput(snapshot.raw));
289
295
  return Boolean(parsed.currentSessionUsed || parsed.currentWeekUsed);
290
296
  }
297
+ function hasClaudeUsageResult(snapshot) {
298
+ return hasClaudeUsageRows(snapshot) || extractClaudeUsageError(snapshot.screen) != null;
299
+ }
300
+ function extractClaudeUsageError(screen, cleanedOutput = "") {
301
+ for (const source of [screen, cleanedOutput]) {
302
+ for (const line of compactLines(source)) {
303
+ const errorMatch = /^Error:\s*(.+)$/i.exec(line);
304
+ if (errorMatch?.[1]) {
305
+ return errorMatch[1].trim();
306
+ }
307
+ if (isClaudeAuthErrorLine(line)) {
308
+ return line;
309
+ }
310
+ }
311
+ }
312
+ return null;
313
+ }
314
+ function isClaudeAuthErrorLine(line) {
315
+ return /\b(?:auth(?:entication)?|credentials?|login|logged in|token)\b/i.test(line) && /\b(?:error|expired|failed|invalid|missing|not|please|required|sign in)\b/i.test(line);
316
+ }
291
317
  function buildClaudeUsageLimits(parsed, context) {
292
318
  const sessionUsed = parsePercentUsed(parsed.currentSessionUsed);
293
319
  const weekUsed = parsePercentUsed(parsed.currentWeekUsed);