@agentuity/cli 0.1.14 → 0.1.16

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 (158) hide show
  1. package/dist/auth.d.ts.map +1 -1
  2. package/dist/auth.js +7 -6
  3. package/dist/auth.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +69 -11
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/index.d.ts.map +1 -1
  8. package/dist/cmd/ai/index.js +6 -1
  9. package/dist/cmd/ai/index.js.map +1 -1
  10. package/dist/cmd/ai/opencode/index.d.ts +3 -0
  11. package/dist/cmd/ai/opencode/index.d.ts.map +1 -0
  12. package/dist/cmd/ai/opencode/index.js +27 -0
  13. package/dist/cmd/ai/opencode/index.js.map +1 -0
  14. package/dist/cmd/ai/opencode/install.d.ts +3 -0
  15. package/dist/cmd/ai/opencode/install.d.ts.map +1 -0
  16. package/dist/cmd/ai/opencode/install.js +102 -0
  17. package/dist/cmd/ai/opencode/install.js.map +1 -0
  18. package/dist/cmd/ai/opencode/run.d.ts +3 -0
  19. package/dist/cmd/ai/opencode/run.d.ts.map +1 -0
  20. package/dist/cmd/ai/opencode/run.js +88 -0
  21. package/dist/cmd/ai/opencode/run.js.map +1 -0
  22. package/dist/cmd/ai/opencode/uninstall.d.ts +3 -0
  23. package/dist/cmd/ai/opencode/uninstall.d.ts.map +1 -0
  24. package/dist/cmd/ai/opencode/uninstall.js +82 -0
  25. package/dist/cmd/ai/opencode/uninstall.js.map +1 -0
  26. package/dist/cmd/build/vite/beacon-plugin.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/beacon-plugin.js.map +1 -1
  28. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  29. package/dist/cmd/build/vite/vite-builder.js +1 -1
  30. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  31. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  32. package/dist/cmd/cloud/env/delete.js +87 -34
  33. package/dist/cmd/cloud/env/delete.js.map +1 -1
  34. package/dist/cmd/cloud/env/get.d.ts.map +1 -1
  35. package/dist/cmd/cloud/env/get.js +50 -16
  36. package/dist/cmd/cloud/env/get.js.map +1 -1
  37. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  38. package/dist/cmd/cloud/env/import.js +76 -32
  39. package/dist/cmd/cloud/env/import.js.map +1 -1
  40. package/dist/cmd/cloud/env/index.d.ts.map +1 -1
  41. package/dist/cmd/cloud/env/index.js +6 -2
  42. package/dist/cmd/cloud/env/index.js.map +1 -1
  43. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  44. package/dist/cmd/cloud/env/list.js +94 -23
  45. package/dist/cmd/cloud/env/list.js.map +1 -1
  46. package/dist/cmd/cloud/env/org-util.d.ts +16 -0
  47. package/dist/cmd/cloud/env/org-util.d.ts.map +1 -0
  48. package/dist/cmd/cloud/env/org-util.js +28 -0
  49. package/dist/cmd/cloud/env/org-util.js.map +1 -0
  50. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  51. package/dist/cmd/cloud/env/pull.js +61 -29
  52. package/dist/cmd/cloud/env/pull.js.map +1 -1
  53. package/dist/cmd/cloud/env/push.d.ts.map +1 -1
  54. package/dist/cmd/cloud/env/push.js +69 -23
  55. package/dist/cmd/cloud/env/push.js.map +1 -1
  56. package/dist/cmd/cloud/env/set.d.ts.map +1 -1
  57. package/dist/cmd/cloud/env/set.js +69 -26
  58. package/dist/cmd/cloud/env/set.js.map +1 -1
  59. package/dist/cmd/cloud/keyvalue/create-namespace.js +1 -1
  60. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  61. package/dist/cmd/cloud/keyvalue/delete-namespace.js +2 -2
  62. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  63. package/dist/cmd/cloud/keyvalue/delete.js +1 -1
  64. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  65. package/dist/cmd/cloud/keyvalue/get.js +1 -1
  66. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  67. package/dist/cmd/cloud/keyvalue/index.js +1 -1
  68. package/dist/cmd/cloud/keyvalue/index.js.map +1 -1
  69. package/dist/cmd/cloud/keyvalue/keys.js +1 -1
  70. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  71. package/dist/cmd/cloud/keyvalue/list-namespaces.js +1 -1
  72. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  73. package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
  74. package/dist/cmd/cloud/keyvalue/repl.js +8 -5
  75. package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
  76. package/dist/cmd/cloud/keyvalue/search.js +1 -1
  77. package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
  78. package/dist/cmd/cloud/keyvalue/set.js +1 -1
  79. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  80. package/dist/cmd/cloud/keyvalue/stats.js +1 -1
  81. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  82. package/dist/cmd/cloud/keyvalue/util.d.ts +4 -4
  83. package/dist/cmd/cloud/keyvalue/util.d.ts.map +1 -1
  84. package/dist/cmd/cloud/keyvalue/util.js +4 -9
  85. package/dist/cmd/cloud/keyvalue/util.js.map +1 -1
  86. package/dist/cmd/project/create.d.ts.map +1 -1
  87. package/dist/cmd/project/create.js +20 -2
  88. package/dist/cmd/project/create.js.map +1 -1
  89. package/dist/cmd/project/download.d.ts +3 -1
  90. package/dist/cmd/project/download.d.ts.map +1 -1
  91. package/dist/cmd/project/download.js +5 -0
  92. package/dist/cmd/project/download.js.map +1 -1
  93. package/dist/cmd/project/template-flow.d.ts +5 -0
  94. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  95. package/dist/cmd/project/template-flow.js +188 -79
  96. package/dist/cmd/project/template-flow.js.map +1 -1
  97. package/dist/cmd/setup/index.d.ts.map +1 -1
  98. package/dist/cmd/setup/index.js +2 -1
  99. package/dist/cmd/setup/index.js.map +1 -1
  100. package/dist/onboarding/agentPrompt.d.ts +8 -0
  101. package/dist/onboarding/agentPrompt.d.ts.map +1 -0
  102. package/dist/onboarding/agentPrompt.js +263 -0
  103. package/dist/onboarding/agentPrompt.js.map +1 -0
  104. package/dist/schema-generator.d.ts +1 -1
  105. package/dist/schema-generator.d.ts.map +1 -1
  106. package/dist/schema-parser.d.ts +1 -1
  107. package/dist/schema-parser.d.ts.map +1 -1
  108. package/dist/schema-parser.js +36 -1
  109. package/dist/schema-parser.js.map +1 -1
  110. package/dist/tui/prompt.d.ts.map +1 -1
  111. package/dist/tui/prompt.js +7 -1
  112. package/dist/tui/prompt.js.map +1 -1
  113. package/dist/tui.d.ts.map +1 -1
  114. package/dist/tui.js +91 -28
  115. package/dist/tui.js.map +1 -1
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/types.js.map +1 -1
  118. package/package.json +7 -7
  119. package/src/auth.ts +7 -6
  120. package/src/cli.ts +84 -11
  121. package/src/cmd/ai/index.ts +6 -1
  122. package/src/cmd/ai/opencode/index.ts +28 -0
  123. package/src/cmd/ai/opencode/install.ts +120 -0
  124. package/src/cmd/ai/opencode/run.ts +103 -0
  125. package/src/cmd/ai/opencode/uninstall.ts +90 -0
  126. package/src/cmd/build/vite/beacon-plugin.ts +3 -1
  127. package/src/cmd/build/vite/vite-builder.ts +5 -1
  128. package/src/cmd/cloud/env/delete.ts +100 -41
  129. package/src/cmd/cloud/env/get.ts +53 -16
  130. package/src/cmd/cloud/env/import.ts +86 -37
  131. package/src/cmd/cloud/env/index.ts +6 -2
  132. package/src/cmd/cloud/env/list.ts +102 -27
  133. package/src/cmd/cloud/env/org-util.ts +37 -0
  134. package/src/cmd/cloud/env/pull.ts +67 -31
  135. package/src/cmd/cloud/env/push.ts +81 -28
  136. package/src/cmd/cloud/env/set.ts +82 -33
  137. package/src/cmd/cloud/keyvalue/create-namespace.ts +1 -1
  138. package/src/cmd/cloud/keyvalue/delete-namespace.ts +2 -2
  139. package/src/cmd/cloud/keyvalue/delete.ts +1 -1
  140. package/src/cmd/cloud/keyvalue/get.ts +1 -1
  141. package/src/cmd/cloud/keyvalue/index.ts +1 -1
  142. package/src/cmd/cloud/keyvalue/keys.ts +1 -1
  143. package/src/cmd/cloud/keyvalue/list-namespaces.ts +1 -1
  144. package/src/cmd/cloud/keyvalue/repl.ts +8 -5
  145. package/src/cmd/cloud/keyvalue/search.ts +1 -1
  146. package/src/cmd/cloud/keyvalue/set.ts +1 -1
  147. package/src/cmd/cloud/keyvalue/stats.ts +1 -1
  148. package/src/cmd/cloud/keyvalue/util.ts +8 -17
  149. package/src/cmd/project/create.ts +21 -2
  150. package/src/cmd/project/download.ts +7 -1
  151. package/src/cmd/project/template-flow.ts +215 -80
  152. package/src/cmd/setup/index.ts +2 -1
  153. package/src/onboarding/agentPrompt.ts +263 -0
  154. package/src/schema-generator.ts +1 -1
  155. package/src/schema-parser.ts +42 -3
  156. package/src/tui/prompt.ts +10 -3
  157. package/src/tui.ts +95 -31
  158. package/src/types.ts +1 -0
package/src/auth.ts CHANGED
@@ -104,8 +104,13 @@ export async function optionalAuth(
104
104
  return auth;
105
105
  }
106
106
 
107
- // Skip interactive prompts if requested (e.g., --confirm flag in CI/scripts)
107
+ // Skip interactive prompts if requested (e.g., --confirm flag, --no-register in CI/scripts)
108
+ // Still show the logged out message to inform user about limited capabilities
108
109
  if (skipPrompts) {
110
+ if (isTTY()) {
111
+ const hasLoggedIn = await hasLoggedInBefore();
112
+ tui.showLoggedOutMessage(getAppBaseURL(ctx.config ?? null), hasLoggedIn);
113
+ }
109
114
  return null;
110
115
  }
111
116
 
@@ -269,11 +274,7 @@ export async function optionalOrg(
269
274
  return undefined;
270
275
  }
271
276
 
272
- // Skip org selection if --no-register is explicitly set (e.g., create command)
273
- // Type assertion: register is a command-specific option not in GlobalOptions
274
- if ('register' in options && (options as { register?: boolean }).register === false) {
275
- return undefined;
276
- }
277
+ // Note: --no-register check is handled in cli.ts before calling this function
277
278
 
278
279
  // Check if org is provided via --org-id flag
279
280
  if (options.orgId) {
package/src/cli.ts CHANGED
@@ -973,6 +973,10 @@ async function registerSubcommand(
973
973
  arrayDesc,
974
974
  (value: string, previous: string[]) => (previous ?? []).concat([value])
975
975
  );
976
+ } else if (opt.type === 'optionalString') {
977
+ // Optional string: --flag uses true, --flag=value uses the string value
978
+ // In Commander.js, [value] means optional argument
979
+ cmd.option(`${flagSpec} [${opt.name}]`, desc);
976
980
  } else {
977
981
  const strDefault = opt.hasDefault
978
982
  ? typeof opt.defaultValue === 'function'
@@ -1019,7 +1023,16 @@ async function registerSubcommand(
1019
1023
  try {
1020
1024
  const auth = await requireAuth(baseCtx);
1021
1025
  if (auth) {
1022
- const apiClient = createAPIClient(baseCtx, baseCtx.config);
1026
+ // Create config with auth credentials for API client
1027
+ const configWithAuth = {
1028
+ ...baseCtx.config,
1029
+ auth: {
1030
+ api_key: auth.apiKey,
1031
+ user_id: auth.userId,
1032
+ expires: auth.expires.getTime(),
1033
+ },
1034
+ };
1035
+ const apiClient = createAPIClient(baseCtx, configWithAuth as Config);
1023
1036
  const { projectGet } = await import('@agentuity/server');
1024
1037
  const projectDetails = await projectGet(apiClient, { id: projectId, mask: true });
1025
1038
  project = {
@@ -1155,16 +1168,32 @@ async function registerSubcommand(
1155
1168
  ctx as CommandContext & { apiClient: APIClientType }
1156
1169
  );
1157
1170
  }
1158
- if (normalized.optionalOrg && ctx.auth) {
1171
+ // Skip org handling if --no-register is set (org only needed for registration)
1172
+ const skipOrg =
1173
+ normalized.optionalOrg &&
1174
+ !normalized.requiresOrg &&
1175
+ ctx.opts &&
1176
+ (ctx.opts as Record<string, unknown>).register === false;
1177
+
1178
+ if (normalized.optionalOrg && ctx.auth && !skipOrg) {
1159
1179
  ctx.orgId = await selectOptionalOrg(
1160
1180
  ctx as CommandContext & { apiClient: APIClientType }
1161
1181
  );
1162
1182
  }
1183
+ // Skip region handling if --no-register is set (region only needed for registration)
1184
+ const skipRegion =
1185
+ normalized.optionalRegion &&
1186
+ !normalized.requiresRegion &&
1187
+ !normalized.requiresRegions &&
1188
+ ctx.opts &&
1189
+ (ctx.opts as Record<string, unknown>).register === false;
1190
+
1163
1191
  if (
1164
1192
  (normalized.requiresRegion ||
1165
1193
  normalized.optionalRegion ||
1166
1194
  normalized.requiresRegions) &&
1167
- ctx.apiClient
1195
+ ctx.apiClient &&
1196
+ !skipRegion
1168
1197
  ) {
1169
1198
  const apiClient: APIClientType = ctx.apiClient as APIClientType;
1170
1199
  const regions = await tui.spinner({
@@ -1241,16 +1270,32 @@ async function registerSubcommand(
1241
1270
  if (normalized.requiresOrg) {
1242
1271
  ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1243
1272
  }
1244
- if (normalized.optionalOrg && ctx.auth) {
1273
+ // Skip org handling if --no-register is set (org only needed for registration)
1274
+ const skipOrg =
1275
+ normalized.optionalOrg &&
1276
+ !normalized.requiresOrg &&
1277
+ ctx.opts &&
1278
+ (ctx.opts as Record<string, unknown>).register === false;
1279
+
1280
+ if (normalized.optionalOrg && ctx.auth && !skipOrg) {
1245
1281
  ctx.orgId = await selectOptionalOrg(
1246
1282
  ctx as CommandContext & { apiClient: APIClientType }
1247
1283
  );
1248
1284
  }
1285
+ // Skip region handling if --no-register is set (region only needed for registration)
1286
+ const skipRegion =
1287
+ normalized.optionalRegion &&
1288
+ !normalized.requiresRegion &&
1289
+ !normalized.requiresRegions &&
1290
+ ctx.opts &&
1291
+ (ctx.opts as Record<string, unknown>).register === false;
1292
+
1249
1293
  if (
1250
1294
  (normalized.requiresRegion ||
1251
1295
  normalized.optionalRegion ||
1252
1296
  normalized.requiresRegions) &&
1253
- ctx.apiClient
1297
+ ctx.apiClient &&
1298
+ !skipRegion
1254
1299
  ) {
1255
1300
  const apiClient: APIClientType = ctx.apiClient as APIClientType;
1256
1301
  const regions = await tui.spinner({
@@ -1322,8 +1367,8 @@ async function registerSubcommand(
1322
1367
  );
1323
1368
  }
1324
1369
 
1325
- // Check if --confirm flag is set to skip interactive prompts
1326
- const skipPrompts = options.confirm === true;
1370
+ // Check if --confirm flag or --no-register flag is set to skip interactive prompts
1371
+ const skipPrompts = options.confirm === true || options.register === false;
1327
1372
  const auth = await optionalAuth(
1328
1373
  baseCtx as CommandContext<undefined>,
1329
1374
  continueText,
@@ -1375,16 +1420,31 @@ async function registerSubcommand(
1375
1420
  ctx as CommandContext & { apiClient: APIClientType }
1376
1421
  );
1377
1422
  }
1378
- if (normalized.optionalOrg && ctx.apiClient && auth) {
1423
+ // Skip org handling if --no-register is set (org only needed for registration)
1424
+ const skipOrg =
1425
+ normalized.optionalOrg &&
1426
+ !normalized.requiresOrg &&
1427
+ ctx.opts &&
1428
+ (ctx.opts as Record<string, unknown>).register === false;
1429
+
1430
+ if (normalized.optionalOrg && ctx.apiClient && auth && !skipOrg) {
1379
1431
  ctx.orgId = await selectOptionalOrg(
1380
1432
  ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
1381
1433
  );
1382
1434
  baseCtx.logger.trace('selected orgId: %s', ctx.orgId);
1383
1435
  }
1436
+ // Skip region handling if --no-register is set (region only needed for registration)
1437
+ const skipRegion =
1438
+ normalized.optionalRegion &&
1439
+ !normalized.requiresRegion &&
1440
+ ctx.opts &&
1441
+ (ctx.opts as Record<string, unknown>).register === false;
1442
+
1384
1443
  if (
1385
1444
  (normalized.requiresRegion || normalized.optionalRegion) &&
1386
1445
  ctx.apiClient &&
1387
- auth
1446
+ auth &&
1447
+ !skipRegion
1388
1448
  ) {
1389
1449
  const apiClient: APIClientType = ctx.apiClient as APIClientType;
1390
1450
  const regions = await tui.spinner({
@@ -1455,12 +1515,25 @@ async function registerSubcommand(
1455
1515
  if (normalized.requiresOrg && ctx.apiClient) {
1456
1516
  ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1457
1517
  }
1458
- if (normalized.optionalOrg && ctx.apiClient) {
1518
+ // Skip org handling if --no-register is set (org only needed for registration)
1519
+ // For non-schema commands, check options directly (Commander passes all options)
1520
+ const skipOrg =
1521
+ normalized.optionalOrg &&
1522
+ !normalized.requiresOrg &&
1523
+ (options as Record<string, unknown>).register === false;
1524
+
1525
+ if (normalized.optionalOrg && ctx.apiClient && !skipOrg) {
1459
1526
  ctx.orgId = await selectOptionalOrg(
1460
1527
  ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
1461
1528
  );
1462
1529
  }
1463
- if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
1530
+ // Skip region handling if --no-register is set (region only needed for registration)
1531
+ const skipRegion =
1532
+ normalized.optionalRegion &&
1533
+ !normalized.requiresRegion &&
1534
+ (options as Record<string, unknown>).register === false;
1535
+
1536
+ if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient && !skipRegion) {
1464
1537
  const apiClient: APIClientType = ctx.apiClient as APIClientType;
1465
1538
  const regions = await tui.spinner({
1466
1539
  message: 'Fetching cloud regions',
@@ -3,6 +3,7 @@ import capabilitiesCommand from './capabilities';
3
3
  import promptCommand from './prompt';
4
4
  import schemaCommand from './schema';
5
5
  import skillsCommand from './skills';
6
+ import opencodeCommand from './opencode';
6
7
  import { getCommand } from '../../command-prefix';
7
8
 
8
9
  export const command = createCommand({
@@ -11,6 +12,10 @@ export const command = createCommand({
11
12
  skipUpgradeCheck: true,
12
13
  tags: ['fast'],
13
14
  examples: [
15
+ {
16
+ command: getCommand('ai opencode install'),
17
+ description: 'Install Agentuity Open Code plugin',
18
+ },
14
19
  {
15
20
  command: getCommand('ai capabilities show'),
16
21
  description: 'Show CLI capabilities for AI agents',
@@ -24,5 +29,5 @@ export const command = createCommand({
24
29
  description: 'Generate Agent Skills from CLI schema',
25
30
  },
26
31
  ],
27
- subcommands: [capabilitiesCommand, promptCommand, schemaCommand, skillsCommand],
32
+ subcommands: [opencodeCommand, capabilitiesCommand, promptCommand, schemaCommand, skillsCommand],
28
33
  });
@@ -0,0 +1,28 @@
1
+ import { createCommand } from '../../../types';
2
+ import { installSubcommand } from './install';
3
+ import { uninstallSubcommand } from './uninstall';
4
+ import { runSubcommand } from './run';
5
+ import { getCommand } from '../../../command-prefix';
6
+
7
+ export const command = createCommand({
8
+ name: 'opencode',
9
+ description: 'Agentuity Open Code plugin - AI coding agent team',
10
+ tags: ['fast'],
11
+ examples: [
12
+ {
13
+ command: getCommand('ai opencode install'),
14
+ description: 'Install Agentuity Open Code plugin',
15
+ },
16
+ {
17
+ command: getCommand('ai opencode uninstall'),
18
+ description: 'Uninstall Agentuity Open Code plugin',
19
+ },
20
+ {
21
+ command: getCommand('ai opencode run "implement dark mode"'),
22
+ description: 'Run a task with the Agentuity Coder agent team',
23
+ },
24
+ ],
25
+ subcommands: [installSubcommand, uninstallSubcommand, runSubcommand],
26
+ });
27
+
28
+ export default command;
@@ -0,0 +1,120 @@
1
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { createSubcommand, type CommandContext } from '../../../types';
5
+ import * as tui from '../../../tui';
6
+ import { getCommand } from '../../../command-prefix';
7
+
8
+ const OPENCODE_CONFIG_DIR = join(homedir(), '.config', 'opencode');
9
+ const OPENCODE_CONFIG_FILE = join(OPENCODE_CONFIG_DIR, 'opencode.json');
10
+
11
+ export const installSubcommand = createSubcommand({
12
+ name: 'install',
13
+ description: 'Install Agentuity Open Code plugin',
14
+ tags: ['fast'],
15
+ requires: { auth: true, apiClient: true, org: true },
16
+ examples: [
17
+ {
18
+ command: getCommand('ai opencode install'),
19
+ description: 'Install Agentuity Open Code plugin',
20
+ },
21
+ ],
22
+ async handler(ctx: CommandContext<{ auth: true; apiClient: true; org: true }>) {
23
+ const { options, orgId } = ctx;
24
+ const jsonMode = !!options?.json;
25
+
26
+ if (!jsonMode) {
27
+ tui.newline();
28
+ tui.output(tui.bold('Installing Agentuity Open Code plugin'));
29
+ tui.newline();
30
+ tui.success(`Using organization: ${orgId}`);
31
+ }
32
+
33
+ const pluginEntry = '@agentuity/opencode';
34
+
35
+ // Update Open Code config if needed
36
+ let openCodeUpdated = false;
37
+ if (!existsSync(OPENCODE_CONFIG_DIR)) {
38
+ mkdirSync(OPENCODE_CONFIG_DIR, { recursive: true });
39
+ }
40
+
41
+ let openCodeConfig: { plugin?: string[]; $schema?: string } = {};
42
+ if (existsSync(OPENCODE_CONFIG_FILE)) {
43
+ try {
44
+ const content = readFileSync(OPENCODE_CONFIG_FILE, 'utf-8');
45
+ openCodeConfig = JSON.parse(content);
46
+ } catch {
47
+ openCodeConfig = {};
48
+ }
49
+ }
50
+
51
+ if (!openCodeConfig.plugin) {
52
+ openCodeConfig.plugin = [];
53
+ }
54
+
55
+ // Check if the exact plugin entry already exists
56
+ const hasExactEntry = openCodeConfig.plugin.includes(pluginEntry);
57
+
58
+ // Check if there's an existing entry that needs updating
59
+ const existingIndex = openCodeConfig.plugin.findIndex((p) => p === '@agentuity/opencode');
60
+
61
+ if (hasExactEntry) {
62
+ if (!jsonMode) {
63
+ tui.info('Open Code plugin already configured');
64
+ }
65
+ } else if (existingIndex >= 0) {
66
+ // Update existing entry to new value
67
+ openCodeConfig.plugin[existingIndex] = pluginEntry;
68
+ writeFileSync(OPENCODE_CONFIG_FILE, JSON.stringify(openCodeConfig, null, 2));
69
+ if (!jsonMode) {
70
+ tui.success(`Updated Open Code plugin to: ${pluginEntry}`);
71
+ }
72
+ openCodeUpdated = true;
73
+ } else {
74
+ // Add new entry
75
+ openCodeConfig.plugin.push(pluginEntry);
76
+ writeFileSync(OPENCODE_CONFIG_FILE, JSON.stringify(openCodeConfig, null, 2));
77
+ if (!jsonMode) {
78
+ tui.success(`Added ${pluginEntry} to Open Code config`);
79
+ }
80
+ openCodeUpdated = true;
81
+ }
82
+
83
+ // Summary (TUI only)
84
+ if (!jsonMode) {
85
+ tui.newline();
86
+ if (openCodeUpdated) {
87
+ tui.output(tui.bold('Agentuity Open Code plugin installed successfully!'));
88
+ } else {
89
+ tui.output(tui.bold('Agentuity Open Code plugin already installed'));
90
+ }
91
+
92
+ tui.newline();
93
+ tui.info('Next steps:');
94
+ tui.output(` ${tui.ICONS.bullet} Start Open Code to use Agentuity Coder agents`);
95
+ tui.output(
96
+ ` ${tui.ICONS.bullet} Run ${tui.bold(getCommand('ai opencode run "<task>"'))} to execute tasks`
97
+ );
98
+ tui.newline();
99
+
100
+ tui.output(tui.muted('Recommended MCP servers for Scout/Expert agents:'));
101
+ tui.output(tui.muted(`Add to your opencode.json (${OPENCODE_CONFIG_FILE}):`));
102
+ tui.newline();
103
+ tui.output(tui.muted(' "mcp": {'));
104
+ tui.output(
105
+ tui.muted(
106
+ ' "context7": { "type": "remote", "url": "https://mcp.context7.com/mcp" },'
107
+ )
108
+ );
109
+ tui.output(
110
+ tui.muted(' "grep_app": { "type": "remote", "url": "https://mcp.grep.app" }')
111
+ );
112
+ tui.output(tui.muted(' }'));
113
+ tui.newline();
114
+ }
115
+
116
+ return { success: true, orgId };
117
+ },
118
+ });
119
+
120
+ export default installSubcommand;
@@ -0,0 +1,103 @@
1
+ import { createSubcommand, type CommandContext } from '../../../types';
2
+ import * as tui from '../../../tui';
3
+ import { getCommand } from '../../../command-prefix';
4
+ import { z } from 'zod';
5
+
6
+ const RunArgsSchema = z.object({
7
+ task: z.string().describe('The task description to execute'),
8
+ });
9
+
10
+ const RunOptionsSchema = z.object({
11
+ sandbox: z.boolean().optional().describe('Run in a cloud sandbox environment'),
12
+ json: z.boolean().optional().describe('Output raw JSON events'),
13
+ });
14
+
15
+ export const runSubcommand = createSubcommand({
16
+ name: 'run',
17
+ description: 'Run a task with the Agentuity Coder agent team (non-interactive)',
18
+ tags: ['slow'],
19
+ requires: { auth: true },
20
+ schema: {
21
+ args: RunArgsSchema,
22
+ options: RunOptionsSchema,
23
+ },
24
+ examples: [
25
+ {
26
+ command: getCommand('ai opencode run "implement dark mode toggle"'),
27
+ description: 'Run a coding task',
28
+ },
29
+ {
30
+ command: getCommand('ai opencode run --sandbox "run the test suite"'),
31
+ description: 'Run in a cloud sandbox',
32
+ },
33
+ {
34
+ command: getCommand('ai opencode run --json "fix the bug"'),
35
+ description: 'Output raw JSON events',
36
+ },
37
+ ],
38
+ async handler(
39
+ ctx: CommandContext<{ auth: true }, undefined, typeof RunArgsSchema, typeof RunOptionsSchema>
40
+ ) {
41
+ const { logger, args, opts } = ctx;
42
+ const { task } = args;
43
+ const sandbox = opts?.sandbox;
44
+ const json = opts?.json;
45
+
46
+ if (!json) {
47
+ tui.newline();
48
+ tui.output(tui.bold('Agentuity Coder'));
49
+ tui.newline();
50
+ tui.output(`${tui.ICONS.arrow} Running task: ${task}`);
51
+ if (sandbox) {
52
+ tui.info('Sandbox mode enabled');
53
+ }
54
+ tui.newline();
55
+ }
56
+
57
+ const openCodeArgs = ['run', '--agent', 'Agentuity Coder Lead'];
58
+
59
+ if (json) {
60
+ openCodeArgs.push('--format', 'json');
61
+ }
62
+
63
+ // Build the task prompt with any mode prefixes
64
+ let taskPrompt = task;
65
+ if (sandbox) {
66
+ taskPrompt = `[SANDBOX MODE: Execute all code in an Agentuity cloud sandbox, not locally] ${taskPrompt}`;
67
+ }
68
+ if (json) {
69
+ taskPrompt = `[JSON OUTPUT: Your final response MUST be a valid JSON object with this structure: {"status": "success" | "failed" | "partial", "summary": "Brief description of what was done", "filesChanged": ["path/to/file.ts"], "errors": ["error message if any"], "payload": <any task-specific return data or null>}. Output ONLY the JSON, no other text.] ${taskPrompt}`;
70
+ }
71
+
72
+ openCodeArgs.push(taskPrompt);
73
+
74
+ logger.debug(`Spawning: opencode ${openCodeArgs.join(' ')}`);
75
+
76
+ const proc = Bun.spawn(['opencode', ...openCodeArgs], {
77
+ stdio: ['inherit', 'inherit', 'inherit'],
78
+ env: {
79
+ ...process.env,
80
+ AGENTUITY_CODER_MODE: 'non-interactive',
81
+ },
82
+ });
83
+
84
+ const exitCode = await proc.exited;
85
+
86
+ // In JSON mode, let opencode's output speak for itself - exit code indicates success
87
+ if (json) {
88
+ process.exit(exitCode);
89
+ }
90
+
91
+ if (exitCode === 0) {
92
+ tui.newline();
93
+ tui.success('Task completed');
94
+ } else {
95
+ tui.newline();
96
+ tui.error(`Task failed with exit code ${exitCode}`);
97
+ }
98
+
99
+ return { success: exitCode === 0, task, exitCode, sandbox: !!sandbox };
100
+ },
101
+ });
102
+
103
+ export default runSubcommand;
@@ -0,0 +1,90 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { createSubcommand } from '../../../types';
5
+ import * as tui from '../../../tui';
6
+ import { getCommand } from '../../../command-prefix';
7
+
8
+ const OPENCODE_CONFIG_DIR = join(homedir(), '.config', 'opencode');
9
+ const OPENCODE_CONFIG_FILE = join(OPENCODE_CONFIG_DIR, 'opencode.json');
10
+
11
+ export const uninstallSubcommand = createSubcommand({
12
+ name: 'uninstall',
13
+ description: 'Uninstall Agentuity Open Code plugin',
14
+ tags: ['fast'],
15
+ examples: [
16
+ {
17
+ command: getCommand('ai opencode uninstall'),
18
+ description: 'Uninstall Agentuity Open Code plugin',
19
+ },
20
+ ],
21
+ async handler(ctx) {
22
+ const { options } = ctx;
23
+ const jsonMode = !!options?.json;
24
+
25
+ if (!jsonMode) {
26
+ tui.newline();
27
+ tui.output(tui.bold('Uninstalling Agentuity Open Code plugin'));
28
+ tui.newline();
29
+ }
30
+
31
+ let removedFromOpenCode = false;
32
+
33
+ // Remove from OpenCode config
34
+ if (!existsSync(OPENCODE_CONFIG_FILE)) {
35
+ if (!jsonMode) {
36
+ tui.info('Open Code config not found - nothing to uninstall');
37
+ }
38
+ } else {
39
+ try {
40
+ const content = readFileSync(OPENCODE_CONFIG_FILE, 'utf-8');
41
+ const openCodeConfig: { plugin?: string[]; $schema?: string } = JSON.parse(content);
42
+
43
+ if (!openCodeConfig.plugin || openCodeConfig.plugin.length === 0) {
44
+ if (!jsonMode) {
45
+ tui.info('No plugins configured in Open Code');
46
+ }
47
+ } else {
48
+ const originalLength = openCodeConfig.plugin.length;
49
+ openCodeConfig.plugin = openCodeConfig.plugin.filter(
50
+ (p) => p !== '@agentuity/opencode'
51
+ );
52
+
53
+ if (openCodeConfig.plugin.length < originalLength) {
54
+ writeFileSync(OPENCODE_CONFIG_FILE, JSON.stringify(openCodeConfig, null, 2));
55
+ if (!jsonMode) {
56
+ tui.success('Removed Agentuity Open Code plugin');
57
+ }
58
+ removedFromOpenCode = true;
59
+ } else {
60
+ if (!jsonMode) {
61
+ tui.info('Agentuity Open Code plugin not found');
62
+ }
63
+ }
64
+ }
65
+ } catch (error) {
66
+ if (!jsonMode) {
67
+ tui.warn(`Failed to parse Open Code config: ${error}`);
68
+ }
69
+ }
70
+ }
71
+
72
+ if (!jsonMode) {
73
+ tui.newline();
74
+
75
+ if (removedFromOpenCode) {
76
+ tui.output(tui.bold('Agentuity Open Code plugin uninstalled successfully'));
77
+ } else {
78
+ tui.output(tui.bold('Agentuity Open Code plugin was not installed'));
79
+ }
80
+
81
+ tui.newline();
82
+ tui.info(`To reinstall, run: ${tui.bold(getCommand('ai opencode install'))}`);
83
+ tui.newline();
84
+ }
85
+
86
+ return { success: true, removedFromOpenCode };
87
+ },
88
+ });
89
+
90
+ export default uninstallSubcommand;
@@ -104,7 +104,9 @@ export function beaconPlugin(options: BeaconPluginOptions): Plugin {
104
104
  source: beaconCode,
105
105
  });
106
106
  } catch (error) {
107
- this.error(`Failed to read beacon script: ${error instanceof Error ? error.message : String(error)}`);
107
+ this.error(
108
+ `Failed to read beacon script: ${error instanceof Error ? error.message : String(error)}`
109
+ );
108
110
  }
109
111
  },
110
112
 
@@ -142,7 +142,11 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
142
142
  const htmlPath = join(rootDir, 'src', 'web', 'index.html');
143
143
 
144
144
  // Use workbench config passed from runAllBuilds
145
- const { workbenchEnabled = false, workbenchRoute = '/workbench', analyticsEnabled = false } = options;
145
+ const {
146
+ workbenchEnabled = false,
147
+ workbenchRoute = '/workbench',
148
+ analyticsEnabled = false,
149
+ } = options;
146
150
 
147
151
  // Load custom user plugins from agentuity.config.ts if it exists
148
152
  const clientOutDir = join(rootDir, '.agentuity/client');