@smithers-orchestrator/cli 0.20.3 → 0.21.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.
@@ -1,9 +1,11 @@
1
1
  type AgentAvailabilityStatus$1 = "likely-subscription" | "api-key" | "binary-only" | "unavailable";
2
2
 
3
3
  type AgentAvailability$1 = {
4
- id: "claude" | "codex" | "gemini" | "pi" | "kimi" | "amp";
4
+ id: "claude" | "codex" | "antigravity" | "gemini" | "pi" | "kimi" | "amp";
5
5
  displayName: string;
6
6
  binary: string;
7
+ deprecated?: boolean;
8
+ deprecationReason?: string;
7
9
  hasBinary: boolean;
8
10
  hasAuthSignal: boolean;
9
11
  hasApiKeySignal: boolean;
@@ -15,6 +17,15 @@ type AgentAvailability$1 = {
15
17
  unusableReasons: string[];
16
18
  };
17
19
 
20
+ /**
21
+ * Extracts detection-derived provider ids from a generated `.smithers/agents.ts`.
22
+ * Account labels are deliberately ignored; the accounts registry remains the
23
+ * source of truth for account-backed providers.
24
+ *
25
+ * @param {string} source
26
+ * @returns {Set<string>}
27
+ */
28
+ declare function extractGeneratedDetectionProviderIds(source: string): Set<string>;
18
29
  /**
19
30
  * @param {AgentAvailability} agent
20
31
  */
@@ -33,12 +44,14 @@ declare function detectAvailableAgents(env?: NodeJS.ProcessEnv, options?: {
33
44
  }): AgentAvailability[];
34
45
  /**
35
46
  * @param {NodeJS.ProcessEnv} [env]
36
- * @param {{ cwd?: string }} [options]
47
+ * @param {{ cwd?: string; preserveProviderIds?: Iterable<string>; scaffoldProviderIds?: Iterable<string> }} [options]
37
48
  */
38
49
  declare function generateAgentsTs(env?: NodeJS.ProcessEnv, options?: {
39
50
  cwd?: string;
51
+ preserveProviderIds?: Iterable<string>;
52
+ scaffoldProviderIds?: Iterable<string>;
40
53
  }): string;
41
54
  type AgentAvailability = AgentAvailability$1;
42
55
  type AgentAvailabilityStatus = AgentAvailabilityStatus$1;
43
56
 
44
- export { type AgentAvailability, type AgentAvailabilityStatus, describeUnavailableAgent, detectAvailableAgents, formatNoUsableAgentsMessage, generateAgentsTs };
57
+ export { type AgentAvailability, type AgentAvailabilityStatus, describeUnavailableAgent, detectAvailableAgents, extractGeneratedDetectionProviderIds, formatNoUsableAgentsMessage, generateAgentsTs };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @param {string[]} argv
3
+ */
4
+ declare function parseMcpSurfaceArgv(argv: string[]): {
5
+ surface: string;
6
+ argv: string[];
7
+ };
8
+ /**
9
+ * @param {string[]} argv
10
+ * @returns {number}
11
+ */
12
+ declare function findFirstPositionalIndex(argv: string[], startIndex?: number): number;
13
+ /**
14
+ * Incur treats union-typed options as value-bearing flags, so a bare
15
+ * `--resume --run-id value` would consume `--run-id` as the resume value.
16
+ *
17
+ * @param {string[]} argv
18
+ */
19
+ declare function rewriteBareResumeFlagArgv(argv: string[]): string[];
20
+
21
+ export { findFirstPositionalIndex, parseMcpSurfaceArgv, rewriteBareResumeFlagArgv };
@@ -0,0 +1,201 @@
1
+ /**
2
+ * @param {string} value
3
+ * @param {string} fallback
4
+ * @param {number} maxLength
5
+ */
6
+ declare function slugifyEvalToken(value: string, fallback?: string, maxLength?: number): string;
7
+ /**
8
+ * @param {unknown} raw
9
+ * @param {number} index
10
+ */
11
+ declare function normalizeEvalCase(raw: unknown, index: number): {
12
+ id: string;
13
+ name: string;
14
+ input: Record<string, unknown>;
15
+ annotations: Record<string, string | number | boolean>;
16
+ expected: {
17
+ status: string;
18
+ };
19
+ metadata: Record<string, unknown>;
20
+ };
21
+ /**
22
+ * @param {string} root
23
+ * @param {string} path
24
+ * @param {{ maxCases?: number }} [options]
25
+ */
26
+ declare function loadEvalCases(root: string, path: string, options?: {
27
+ maxCases?: number;
28
+ }): {
29
+ path: string;
30
+ cases: {
31
+ id: string;
32
+ name: string;
33
+ input: Record<string, unknown>;
34
+ annotations: Record<string, string | number | boolean>;
35
+ expected: {
36
+ status: string;
37
+ };
38
+ metadata: Record<string, unknown>;
39
+ }[];
40
+ totalCases: number;
41
+ };
42
+ /**
43
+ * @param {string} suiteId
44
+ * @param {string} caseId
45
+ */
46
+ declare function evalRunId(suiteId: string, caseId: string): string;
47
+ /**
48
+ * @param {{
49
+ * suiteId?: string;
50
+ * runLabel?: string;
51
+ * workflowPath: string;
52
+ * casesPath: string;
53
+ * loadedCases: ReturnType<typeof loadEvalCases>;
54
+ * }} input
55
+ */
56
+ declare function buildEvalPlan(input: {
57
+ suiteId?: string;
58
+ runLabel?: string;
59
+ workflowPath: string;
60
+ casesPath: string;
61
+ loadedCases: ReturnType<typeof loadEvalCases>;
62
+ }): {
63
+ suiteId: string;
64
+ runLabel: string | null;
65
+ workflowPath: string;
66
+ casesPath: string;
67
+ totalCases: number;
68
+ plannedCases: number;
69
+ cases: {
70
+ runId: string;
71
+ id: string;
72
+ name: string;
73
+ input: Record<string, unknown>;
74
+ annotations: Record<string, string | number | boolean>;
75
+ expected: {
76
+ status: string;
77
+ };
78
+ metadata: Record<string, unknown>;
79
+ }[];
80
+ };
81
+ /**
82
+ * @param {{ getRun(runId: string): Promise<unknown> }} adapter
83
+ * @param {Array<{ runId: string }>} cases
84
+ */
85
+ declare function assertEvalRunIdsAvailable(adapter: {
86
+ getRun(runId: string): Promise<unknown>;
87
+ }, cases: Array<{
88
+ runId: string;
89
+ }>): Promise<void>;
90
+ /**
91
+ * @param {Array<{ passed: boolean; status?: string; durationMs?: number }>} results
92
+ */
93
+ declare function summarizeEvalResults(results: Array<{
94
+ passed: boolean;
95
+ status?: string;
96
+ durationMs?: number;
97
+ }>): {
98
+ total: number;
99
+ passed: number;
100
+ failed: number;
101
+ byStatus: {};
102
+ durationMs: number;
103
+ };
104
+ /**
105
+ * @param {ReturnType<typeof normalizeEvalCase>} testCase
106
+ * @param {{ status?: string; output?: unknown; error?: unknown }} result
107
+ */
108
+ declare function evaluateEvalCaseResult(testCase: ReturnType<typeof normalizeEvalCase>, result: {
109
+ status?: string;
110
+ output?: unknown;
111
+ error?: unknown;
112
+ }): {
113
+ passed: boolean;
114
+ assertions: {
115
+ name: string;
116
+ passed: boolean;
117
+ expected: any;
118
+ actual: unknown;
119
+ }[];
120
+ };
121
+ /**
122
+ * @param {{
123
+ * plan: ReturnType<typeof buildEvalPlan>;
124
+ * results: Array<Record<string, unknown> & { passed: boolean; status?: string; durationMs?: number }>;
125
+ * startedAtMs: number;
126
+ * finishedAtMs: number;
127
+ * reportPath?: string | null;
128
+ * }} input
129
+ */
130
+ declare function buildEvalReport(input: {
131
+ plan: ReturnType<typeof buildEvalPlan>;
132
+ results: Array<Record<string, unknown> & {
133
+ passed: boolean;
134
+ status?: string;
135
+ durationMs?: number;
136
+ }>;
137
+ startedAtMs: number;
138
+ finishedAtMs: number;
139
+ reportPath?: string | null;
140
+ }): {
141
+ suiteId: string;
142
+ runLabel: string | null;
143
+ workflowPath: string;
144
+ casesPath: string;
145
+ startedAtMs: number;
146
+ finishedAtMs: number;
147
+ durationMs: number;
148
+ reportPath: string | null;
149
+ summary: {
150
+ total: number;
151
+ passed: number;
152
+ failed: number;
153
+ byStatus: {};
154
+ durationMs: number;
155
+ };
156
+ results: (Record<string, unknown> & {
157
+ passed: boolean;
158
+ status?: string;
159
+ durationMs?: number;
160
+ })[];
161
+ };
162
+ /**
163
+ * @param {string} root
164
+ * @param {string} suiteId
165
+ */
166
+ declare function defaultEvalReportPath(root: string, suiteId: string): string;
167
+ /**
168
+ * @param {string} root
169
+ * @param {string} suiteId
170
+ * @param {string | undefined} path
171
+ */
172
+ declare function resolveEvalReportPath(root: string, suiteId: string, path: string | undefined): string;
173
+ /**
174
+ * @param {string} root
175
+ * @param {string} suiteId
176
+ * @param {{ path?: string; force?: boolean }} [options]
177
+ */
178
+ declare function assertEvalReportWritable(root: string, suiteId: string, options?: {
179
+ path?: string;
180
+ force?: boolean;
181
+ }): string;
182
+ /**
183
+ * @param {string} root
184
+ * @param {Record<string, unknown>} report
185
+ * @param {{ path?: string; force?: boolean }} [options]
186
+ */
187
+ declare function writeEvalReport(root: string, report: Record<string, unknown>, options?: {
188
+ path?: string;
189
+ force?: boolean;
190
+ }): string;
191
+ /**
192
+ * @param {ReturnType<typeof buildEvalPlan>} plan
193
+ */
194
+ declare function renderEvalPlan(plan: ReturnType<typeof buildEvalPlan>): string;
195
+ /**
196
+ * @param {ReturnType<typeof buildEvalReport>} report
197
+ */
198
+ declare function renderEvalReport(report: ReturnType<typeof buildEvalReport>): string;
199
+ declare const EVAL_CASE_STATUSES: string[];
200
+
201
+ export { EVAL_CASE_STATUSES, assertEvalReportWritable, assertEvalRunIdsAvailable, buildEvalPlan, buildEvalReport, defaultEvalReportPath, evalRunId, evaluateEvalCaseResult, loadEvalCases, normalizeEvalCase, renderEvalPlan, renderEvalReport, resolveEvalReportPath, slugifyEvalToken, summarizeEvalResults, writeEvalReport };
package/dist/hijack.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _smithers_orchestrator_db_adapter from '@smithers-orchestrator/db/adapter';
2
2
  import { H as HijackCandidate$1 } from './HijackCandidate-FxLeKpcZ.js';
3
3
 
4
- type NativeHijackEngine$1 = "claude-code" | "codex" | "gemini" | "pi" | "kimi" | "forge" | "amp";
4
+ type NativeHijackEngine$1 = "claude-code" | "antigravity" | "codex" | "gemini" | "pi" | "kimi" | "forge" | "amp";
5
5
 
6
6
  type HijackLaunchSpec$1 = {
7
7
  command: string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @param {string | undefined} raw
3
+ * @param {string} label
4
+ * @returns {string | undefined}
5
+ */
6
+ declare function readJsonArgumentPayload(raw: string | undefined, label: string): string | undefined;
7
+ /**
8
+ * @param {string | undefined} raw
9
+ * @param {string} label
10
+ */
11
+ declare function parseJsonArgument(raw: string | undefined, label: string): any;
12
+ /**
13
+ * @param {string | undefined} raw
14
+ * @param {string} label
15
+ * @param {(opts: { code: string; message: string; exitCode: number }) => unknown} fail
16
+ */
17
+ declare function parseJsonInput(raw: string | undefined, label: string, fail: (opts: {
18
+ code: string;
19
+ message: string;
20
+ exitCode: number;
21
+ }) => unknown): any;
22
+ declare const CLI_JSON_ARGUMENT_MAX_BYTES: number;
23
+
24
+ export { CLI_JSON_ARGUMENT_MAX_BYTES, parseJsonArgument, parseJsonInput, readJsonArgumentPayload };
@@ -0,0 +1,8 @@
1
+ declare function smithersTokenStorePath(): string;
2
+ declare function readSmithersTokenStore(): {
3
+ tokens: any;
4
+ };
5
+ declare function writeSmithersTokenStore(store: any): void;
6
+ declare function parseTokenScopes(raw: any): any;
7
+
8
+ export { parseTokenScopes, readSmithersTokenStore, smithersTokenStorePath, writeSmithersTokenStore };
@@ -2,8 +2,12 @@ type WorkflowSourceType$1 = "user" | "seeded" | "generated" | (string & {});
2
2
 
3
3
  type DiscoveredWorkflow$1 = {
4
4
  id: string;
5
+ metadataVersion: 1;
5
6
  displayName: string;
6
7
  sourceType: WorkflowSourceType$1;
8
+ description: string;
9
+ tags: string[];
10
+ aliases: string[];
7
11
  entryFile: string;
8
12
  path: string;
9
13
  };
@@ -29,7 +33,32 @@ declare function resolveWorkflow(id: string, root: string): DiscoveredWorkflow;
29
33
  * @returns {DiscoveredWorkflow}
30
34
  */
31
35
  declare function createWorkflowFile(name: string, root: string): DiscoveredWorkflow;
36
+ /**
37
+ * @param {DiscoveredWorkflow} workflow
38
+ * @param {{ root?: string }} [options]
39
+ * @returns {string}
40
+ */
41
+ declare function renderWorkflowSkill(workflow: DiscoveredWorkflow, options?: {
42
+ root?: string;
43
+ }): string;
44
+ /**
45
+ * @param {string} root
46
+ * @param {{ workflowId?: string; output?: string; force?: boolean }} [options]
47
+ */
48
+ declare function writeWorkflowSkillFiles(root: string, options?: {
49
+ workflowId?: string;
50
+ output?: string;
51
+ force?: boolean;
52
+ }): {
53
+ rootDir: string;
54
+ workflowId: string;
55
+ outputPath: string;
56
+ force: boolean;
57
+ workflows: DiscoveredWorkflow$1[];
58
+ writtenFiles: string[];
59
+ skippedFiles: string[];
60
+ };
32
61
  type WorkflowSourceType = WorkflowSourceType$1;
33
62
  type DiscoveredWorkflow = DiscoveredWorkflow$1;
34
63
 
35
- export { type DiscoveredWorkflow, type WorkflowSourceType, createWorkflowFile, discoverWorkflows, resolveWorkflow, validateWorkflowName };
64
+ export { type DiscoveredWorkflow, type WorkflowSourceType, createWorkflowFile, discoverWorkflows, renderWorkflowSkill, resolveWorkflow, validateWorkflowName, writeWorkflowSkillFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithers-orchestrator/cli",
3
- "version": "0.20.3",
3
+ "version": "0.21.0",
4
4
  "description": "Smithers command-line interface, MCP server, and local workflow tools",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -32,21 +32,21 @@
32
32
  "picocolors": "^1.1.1",
33
33
  "react": "^19.2.5",
34
34
  "zod": "^4.3.6",
35
- "@smithers-orchestrator/accounts": "0.20.3",
36
- "@smithers-orchestrator/agents": "0.20.3",
37
- "@smithers-orchestrator/components": "0.20.3",
38
- "@smithers-orchestrator/db": "0.20.3",
39
- "@smithers-orchestrator/devtools": "0.20.3",
40
- "@smithers-orchestrator/driver": "0.20.3",
41
- "@smithers-orchestrator/engine": "0.20.3",
42
- "@smithers-orchestrator/errors": "0.20.3",
43
- "@smithers-orchestrator/memory": "0.20.3",
44
- "@smithers-orchestrator/observability": "0.20.3",
45
- "@smithers-orchestrator/openapi": "0.20.3",
46
- "@smithers-orchestrator/protocol": "0.20.3",
47
- "@smithers-orchestrator/scheduler": "0.20.3",
48
- "@smithers-orchestrator/server": "0.20.3",
49
- "@smithers-orchestrator/time-travel": "0.20.3"
35
+ "@smithers-orchestrator/accounts": "0.21.0",
36
+ "@smithers-orchestrator/agents": "0.21.0",
37
+ "@smithers-orchestrator/db": "0.21.0",
38
+ "@smithers-orchestrator/driver": "0.21.0",
39
+ "@smithers-orchestrator/devtools": "0.21.0",
40
+ "@smithers-orchestrator/engine": "0.21.0",
41
+ "@smithers-orchestrator/errors": "0.21.0",
42
+ "@smithers-orchestrator/memory": "0.21.0",
43
+ "@smithers-orchestrator/observability": "0.21.0",
44
+ "@smithers-orchestrator/protocol": "0.21.0",
45
+ "@smithers-orchestrator/openapi": "0.21.0",
46
+ "@smithers-orchestrator/server": "0.21.0",
47
+ "@smithers-orchestrator/time-travel": "0.21.0",
48
+ "@smithers-orchestrator/scheduler": "0.21.0",
49
+ "@smithers-orchestrator/components": "0.21.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/bun": "latest",
@@ -1,9 +1,11 @@
1
1
  import type { AgentAvailabilityStatus } from "./AgentAvailabilityStatus.ts";
2
2
 
3
3
  export type AgentAvailability = {
4
- id: "claude" | "codex" | "gemini" | "pi" | "kimi" | "amp";
4
+ id: "claude" | "codex" | "antigravity" | "gemini" | "pi" | "kimi" | "amp";
5
5
  displayName: string;
6
6
  binary: string;
7
+ deprecated?: boolean;
8
+ deprecationReason?: string;
7
9
  hasBinary: boolean;
8
10
  hasAuthSignal: boolean;
9
11
  hasApiKeySignal: boolean;
package/src/AskOptions.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { SmithersToolSurface } from "@smithers-orchestrator/agents/agent-contract";
2
2
 
3
- export type AskAgentId = "claude" | "codex" | "kimi" | "gemini" | "pi";
3
+ export type AskAgentId = "claude" | "codex" | "kimi" | "antigravity" | "gemini" | "pi";
4
4
 
5
5
  export type AskOptions = {
6
6
  agent?: AskAgentId;
@@ -2,8 +2,12 @@ import type { WorkflowSourceType } from "./WorkflowSourceType.ts";
2
2
 
3
3
  export type DiscoveredWorkflow = {
4
4
  id: string;
5
+ metadataVersion: 1;
5
6
  displayName: string;
6
7
  sourceType: WorkflowSourceType;
8
+ description: string;
9
+ tags: string[];
10
+ aliases: string[];
7
11
  entryFile: string;
8
12
  path: string;
9
13
  };
@@ -1,5 +1,6 @@
1
1
  export type NativeHijackEngine =
2
2
  | "claude-code"
3
+ | "antigravity"
3
4
  | "codex"
4
5
  | "gemini"
5
6
  | "pi"
@@ -6,8 +6,9 @@ import { runAgentAdd, pingAccount } from "./runAgentAdd.js";
6
6
 
7
7
  const PROVIDER_CHOICES = [
8
8
  { value: "claude-code", label: "Claude Code (subscription)", hint: "Pro / Max plan via `claude` CLI" },
9
+ { value: "antigravity", label: "Antigravity (subscription)", hint: "Google account via `agy` CLI" },
9
10
  { value: "codex", label: "Codex (subscription)", hint: "ChatGPT Plus/Pro via `codex` CLI" },
10
- { value: "gemini", label: "Gemini (subscription)", hint: "Google account via `gemini` CLI" },
11
+ { value: "gemini", label: "Gemini (deprecated subscription)", hint: "Legacy/enterprise Google account via `gemini` CLI" },
11
12
  { value: "kimi", label: "Kimi (subscription)", hint: "OAuth via `kimi` CLI" },
12
13
  { value: "anthropic-api", label: "Anthropic API key", hint: "Pay-per-token via api.anthropic.com" },
13
14
  { value: "openai-api", label: "OpenAI API key", hint: "Pay-per-token via api.openai.com (used by Codex)" },
@@ -16,6 +17,7 @@ const PROVIDER_CHOICES = [
16
17
 
17
18
  const SUBSCRIPTION_LOGIN_BIN = {
18
19
  "claude-code": "claude",
20
+ "antigravity": "agy",
19
21
  "codex": "codex",
20
22
  "gemini": "gemini",
21
23
  "kimi": "kimi",
@@ -23,6 +25,7 @@ const SUBSCRIPTION_LOGIN_BIN = {
23
25
 
24
26
  const SUBSCRIPTION_DIR_ENV_VAR = {
25
27
  "claude-code": "CLAUDE_CONFIG_DIR",
28
+ "antigravity": "GEMINI_DIR",
26
29
  "codex": "CODEX_HOME",
27
30
  "gemini": "GEMINI_DIR",
28
31
  "kimi": "KIMI_SHARE_DIR",
@@ -33,15 +36,24 @@ const SUBSCRIPTION_DIR_ENV_VAR = {
33
36
  * dedicated subcommand (`codex login`, `kimi login`); others authenticate via
34
37
  * a slash command inside the REPL (`claude` then /login).
35
38
  *
36
- * @type {Record<string, { args: string[]; postInstructions?: string }>}
39
+ * @type {Record<string, { args: string[] | ((configDir: string) => string[]); postInstructions?: string }>}
37
40
  */
38
41
  const SUBSCRIPTION_LOGIN_RECIPE = {
39
42
  "claude-code": { args: [], postInstructions: "Inside Claude Code, type /login and follow the browser flow." },
43
+ "antigravity": { args: (configDir) => ["--gemini_dir", configDir], postInstructions: "Antigravity will open Google Sign-In or print a browser authorization URL." },
40
44
  "codex": { args: ["login"] },
41
45
  "gemini": { args: [], postInstructions: "Inside Gemini, type /auth (or follow the prompt) to sign in." },
42
46
  "kimi": { args: ["login"] },
43
47
  };
44
48
 
49
+ /**
50
+ * @param {string[] | ((configDir: string) => string[])} args
51
+ * @param {string} configDir
52
+ */
53
+ function resolveLoginArgs(args, configDir) {
54
+ return typeof args === "function" ? args(configDir) : args;
55
+ }
56
+
45
57
  function bail() {
46
58
  outro("Cancelled.");
47
59
  process.exit(130);
@@ -102,7 +114,8 @@ export async function agentAddWizard(opts = {}) {
102
114
  const bin = SUBSCRIPTION_LOGIN_BIN[provider];
103
115
  const envVar = SUBSCRIPTION_DIR_ENV_VAR[provider];
104
116
  const recipe = SUBSCRIPTION_LOGIN_RECIPE[provider] ?? { args: [] };
105
- const loginCmd = `${envVar}=${configDir} ${bin}${recipe.args.length ? " " + recipe.args.join(" ") : ""}`;
117
+ const loginArgs = resolveLoginArgs(recipe.args, configDir);
118
+ const loginCmd = `${envVar}=${configDir} ${bin}${loginArgs.length ? " " + loginArgs.join(" ") : ""}`;
106
119
  const lines = [
107
120
  "Open another terminal and run:",
108
121
  "",
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { generateAgentsTs } from "../agent-detection.js";
3
+ import { extractGeneratedDetectionProviderIds, generateAgentsTs } from "../agent-detection.js";
4
4
 
5
5
  /**
6
6
  * If the current working directory contains a `.smithers/agents.ts` that was
@@ -19,7 +19,20 @@ export function regenerateAgentsTsIfPresent(cwd = process.cwd()) {
19
19
  if (!existing.startsWith("// smithers-source: generated")) {
20
20
  return { rewritten: false, path, reason: "agents.ts has been edited by hand; not overwriting" };
21
21
  }
22
- const next = generateAgentsTs(process.env);
22
+ const scaffoldFiles = {
23
+ claude: "claude-code.ts",
24
+ codex: "codex.ts",
25
+ antigravity: "antigravity.ts",
26
+ gemini: "gemini.ts",
27
+ };
28
+ const scaffoldProviderIds = Object.entries(scaffoldFiles)
29
+ .filter(([, file]) => existsSync(join(cwd, ".smithers", "agents", file)))
30
+ .map(([providerId]) => providerId);
31
+ const next = generateAgentsTs(process.env, {
32
+ cwd,
33
+ preserveProviderIds: extractGeneratedDetectionProviderIds(existing),
34
+ scaffoldProviderIds,
35
+ });
23
36
  if (next === existing) {
24
37
  return { rewritten: false, path, reason: "no changes" };
25
38
  }
@@ -12,6 +12,7 @@ import { regenerateAgentsTsIfPresent } from "./regenerateAgentsTsIfPresent.js";
12
12
  */
13
13
  const SUBSCRIPTION_LOGIN_BIN = {
14
14
  "claude-code": "claude",
15
+ "antigravity": "agy",
15
16
  "codex": "codex",
16
17
  "gemini": "gemini",
17
18
  "kimi": "kimi",
@@ -24,10 +25,11 @@ const SUBSCRIPTION_LOGIN_BIN = {
24
25
  * Subcommand args appended to the login command. Some CLIs use a dedicated
25
26
  * subcommand (`codex login`, `kimi login`); others authenticate via a slash
26
27
  * command inside the REPL (the user types /login after launching).
27
- * @type {Record<string, string[]>}
28
+ * @type {Record<string, string[] | ((configDir: string) => string[])>}
28
29
  */
29
30
  const SUBSCRIPTION_LOGIN_ARGS = {
30
31
  "claude-code": [],
32
+ "antigravity": (configDir) => ["--gemini_dir", configDir],
31
33
  "codex": ["login"],
32
34
  "gemini": [],
33
35
  "kimi": ["login"],
@@ -39,6 +41,7 @@ const SUBSCRIPTION_LOGIN_ARGS = {
39
41
  */
40
42
  const SUBSCRIPTION_DIR_ENV_VAR = {
41
43
  "claude-code": "CLAUDE_CONFIG_DIR",
44
+ "antigravity": "GEMINI_DIR",
42
45
  "codex": "CODEX_HOME",
43
46
  "gemini": "GEMINI_DIR",
44
47
  "kimi": "KIMI_SHARE_DIR",
@@ -47,6 +50,15 @@ const SUBSCRIPTION_DIR_ENV_VAR = {
47
50
  "gemini-api": null,
48
51
  };
49
52
 
53
+ /**
54
+ * @param {string} provider
55
+ * @param {string} configDir
56
+ */
57
+ function subscriptionLoginArgs(provider, configDir) {
58
+ const args = SUBSCRIPTION_LOGIN_ARGS[provider] ?? [];
59
+ return typeof args === "function" ? args(configDir) : args;
60
+ }
61
+
50
62
  /**
51
63
  * @param {string} dir
52
64
  * @returns {boolean}
@@ -94,7 +106,7 @@ export function runAgentAdd(input) {
94
106
  if (!populated && !input.skipLogin && !input.force) {
95
107
  const bin = SUBSCRIPTION_LOGIN_BIN[input.provider];
96
108
  const envVar = SUBSCRIPTION_DIR_ENV_VAR[input.provider];
97
- const subArgs = SUBSCRIPTION_LOGIN_ARGS[input.provider] ?? [];
109
+ const subArgs = subscriptionLoginArgs(input.provider, configDir);
98
110
  const cmd = `${envVar}=${configDir} ${bin}${subArgs.length ? " " + subArgs.join(" ") : ""}`;
99
111
  const detail = `Config dir ${configDir} is empty. Run the following in another terminal to log in, then re-run \`smithers agents add\`:\n\n ${cmd}\n\n(or pass --skip-login to register the empty dir, --force to register without verification)`;
100
112
  return { ok: false, reason: "login-required", detail, configDir };