suzi-cli 0.1.22 → 0.1.24

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.
@@ -47,6 +47,14 @@ exports.executeAgentCommand = executeAgentCommand;
47
47
  exports.logsAgentCommand = logsAgentCommand;
48
48
  exports.codeAgentCommand = codeAgentCommand;
49
49
  exports.deleteAgentCommand = deleteAgentCommand;
50
+ exports.historyCommand = historyCommand;
51
+ exports.diffCommand = diffCommand;
52
+ exports.versionCommand = versionCommand;
53
+ exports.checkoutCommand = checkoutCommand;
54
+ exports.forkCommand = forkCommand;
55
+ exports.upstreamCommand = upstreamCommand;
56
+ exports.pullCommand = pullCommand;
57
+ exports.listForksCommand = listForksCommand;
50
58
  const commander_1 = require("commander");
51
59
  const chalk_1 = __importDefault(require("chalk"));
52
60
  const cli_table3_1 = __importDefault(require("cli-table3"));
@@ -54,11 +62,47 @@ const inquirer_1 = __importDefault(require("inquirer"));
54
62
  const fs_1 = __importDefault(require("fs"));
55
63
  const path_1 = __importDefault(require("path"));
56
64
  const api_1 = require("../lib/api");
65
+ const api_errors_1 = require("../lib/api-errors");
57
66
  const ui_1 = require("../utils/ui");
58
67
  const memory_1 = require("../lib/memory");
59
68
  const tty_1 = require("../utils/tty");
60
69
  const agent_picker_1 = require("../utils/agent-picker");
61
70
  const session_registry_1 = require("../lib/session-registry");
71
+ function outputCliApiErrorJson(summary, raw) {
72
+ (0, tty_1.outputJson)({
73
+ success: false,
74
+ error: summary,
75
+ data: {
76
+ error: raw,
77
+ },
78
+ });
79
+ }
80
+ function printCliApiIssues(issues, heading = ' Issues:') {
81
+ if (issues.length === 0) {
82
+ return;
83
+ }
84
+ console.log();
85
+ console.log(ui_1.colors.error(heading));
86
+ for (const issue of issues) {
87
+ console.log(` ${ui_1.colors.error('✕')} ${(0, api_errors_1.formatCliApiErrorIssue)(issue)}`);
88
+ }
89
+ }
90
+ function handleCliApiFailure(raw, options) {
91
+ const parsed = (0, api_errors_1.parseCliApiError)(raw);
92
+ const summary = options.fallbackSummary || parsed.summary || 'Unknown error';
93
+ if (options.jsonMode) {
94
+ outputCliApiErrorJson(summary, parsed.raw);
95
+ return summary;
96
+ }
97
+ (0, ui_1.error)(`${options.prefix || ''}${summary}`);
98
+ if (parsed.issues.length > 0) {
99
+ printCliApiIssues(parsed.issues, options.heading);
100
+ }
101
+ else if (parsed.detailsText) {
102
+ console.log(ui_1.colors.muted(` ${parsed.detailsText}`));
103
+ }
104
+ return summary;
105
+ }
62
106
  function registerAgentCommand(program) {
63
107
  const agent = program
64
108
  .command('agents')
@@ -175,6 +219,72 @@ function registerAgentCommand(program) {
175
219
  .action(async (id, opts) => {
176
220
  await deleteAgentCommand(id, opts);
177
221
  });
222
+ // ── Git-based version control ──
223
+ // suzi agents history [id]
224
+ agent
225
+ .command('history [id]')
226
+ .description('View agent commit history')
227
+ .option('-n, --limit <n>', 'Number of commits to show', '20')
228
+ .option('--json', 'Output as JSON')
229
+ .action(async (id, opts) => {
230
+ await historyCommand(id, opts);
231
+ });
232
+ // suzi agents diff [id] [sha]
233
+ agent
234
+ .command('diff [id] [sha]')
235
+ .description('View diff for a specific commit')
236
+ .option('--json', 'Output as JSON')
237
+ .action(async (id, sha, opts) => {
238
+ await diffCommand(id, sha, opts);
239
+ });
240
+ // suzi agents version [id] [sha]
241
+ agent
242
+ .command('version [id] [sha]')
243
+ .description('View source code at a specific commit')
244
+ .option('--json', 'Output as JSON')
245
+ .action(async (id, sha, opts) => {
246
+ await versionCommand(id, sha, opts);
247
+ });
248
+ // suzi agents checkout [id] [sha]
249
+ agent
250
+ .command('checkout [id] [sha]')
251
+ .description('Revert agent to a previous commit')
252
+ .option('--json', 'Output as JSON')
253
+ .action(async (id, sha, opts) => {
254
+ await checkoutCommand(id, sha, opts);
255
+ });
256
+ // suzi agents fork [id]
257
+ agent
258
+ .command('fork [id]')
259
+ .description('Fork an agent (creates your own copy)')
260
+ .option('--json', 'Output as JSON')
261
+ .action(async (id, opts) => {
262
+ await forkCommand(id, opts);
263
+ });
264
+ // suzi agents upstream [id]
265
+ agent
266
+ .command('upstream [id]')
267
+ .description('Check if upstream has new changes (for forked agents)')
268
+ .option('--json', 'Output as JSON')
269
+ .action(async (id, opts) => {
270
+ await upstreamCommand(id, opts);
271
+ });
272
+ // suzi agents pull [id]
273
+ agent
274
+ .command('pull [id]')
275
+ .description('Pull upstream changes into a forked agent')
276
+ .option('--json', 'Output as JSON')
277
+ .action(async (id, opts) => {
278
+ await pullCommand(id, opts);
279
+ });
280
+ // suzi agents forks [id]
281
+ agent
282
+ .command('forks [id]')
283
+ .description('List all forks of an agent')
284
+ .option('--json', 'Output as JSON')
285
+ .action(async (id, opts) => {
286
+ await listForksCommand(id, opts);
287
+ });
178
288
  }
179
289
  // ============================================================================
180
290
  // Command Implementations
@@ -414,17 +524,10 @@ async function deployAgentCommand(file, opts) {
414
524
  }
415
525
  if (!resp.ok) {
416
526
  spinner?.fail();
417
- const errorMsg = resp.data?.error || resp.data?.message || 'Unknown error';
418
- const details = resp.data?.details;
419
- if (jsonMode) {
420
- (0, tty_1.outputJson)({ success: false, error: errorMsg, data: { details } });
421
- }
422
- else {
423
- (0, ui_1.error)(`Deploy failed: ${errorMsg}`);
424
- if (details) {
425
- console.log(ui_1.colors.muted(` ${details}`));
426
- }
427
- }
527
+ const errorMsg = handleCliApiFailure(resp.data, {
528
+ jsonMode,
529
+ prefix: 'Deploy failed: ',
530
+ });
428
531
  (0, memory_1.appendLearning)({ category: 'error', message: `Deploy failed: ${errorMsg}`, command: 'agent deploy' });
429
532
  return;
430
533
  }
@@ -438,12 +541,35 @@ async function deployAgentCommand(file, opts) {
438
541
  agentData.status = 'active';
439
542
  }
440
543
  else {
441
- const activateError = activateResp.data?.error || activateResp.data?.message || 'Unknown error';
544
+ const parsedError = (0, api_errors_1.parseCliApiError)(activateResp.data);
545
+ const activateError = parsedError.summary;
442
546
  if (jsonMode) {
443
- (0, tty_1.outputJson)({ success: true, data: { agent: agentData, activationError: activateError } });
547
+ (0, tty_1.outputJson)({
548
+ success: false,
549
+ error: activateError,
550
+ data: {
551
+ agent: agentData,
552
+ activationError: {
553
+ summary: activateError,
554
+ error: parsedError.raw,
555
+ },
556
+ },
557
+ });
444
558
  }
445
559
  else {
446
- spinner?.warn(`Agent deployed but activation failed: ${activateError}`);
560
+ if (spinner) {
561
+ spinner.fail(`Agent deployed but activation failed: ${activateError}`);
562
+ process.exitCode = 1;
563
+ }
564
+ else {
565
+ (0, ui_1.error)(`Agent deployed but activation failed: ${activateError}`);
566
+ }
567
+ if (parsedError.issues.length > 0) {
568
+ printCliApiIssues(parsedError.issues);
569
+ }
570
+ else if (parsedError.detailsText) {
571
+ console.log(ui_1.colors.muted(` ${parsedError.detailsText}`));
572
+ }
447
573
  }
448
574
  (0, memory_1.appendLearning)({ category: 'error', message: `Post-deploy activation failed: ${activateError}`, command: 'agent deploy --activate', agentId: agentData.id });
449
575
  return;
@@ -605,18 +731,10 @@ async function validateAgentCommand(file, opts) {
605
731
  const resp = await (0, api_1.post)('/api/agent/validate', { code: source });
606
732
  if (!resp.ok) {
607
733
  spinner?.fail();
608
- const errorData = resp.data;
609
- const msg = errorData?.message || 'Compilation error';
610
- const details = errorData?.details;
611
- if (jsonMode) {
612
- (0, tty_1.outputJson)({ success: false, error: msg, data: { details } });
613
- }
614
- else {
615
- (0, ui_1.error)(msg);
616
- if (details) {
617
- console.log(ui_1.colors.muted(` ${details}`));
618
- }
619
- }
734
+ handleCliApiFailure(resp.data, {
735
+ jsonMode,
736
+ fallbackSummary: 'Compilation error',
737
+ });
620
738
  return;
621
739
  }
622
740
  const data = resp.data;
@@ -678,6 +796,10 @@ async function validateAgentCommand(file, opts) {
678
796
  }
679
797
  }
680
798
  }
799
+ const triggerIssues = data.triggers?.issues || [];
800
+ if (triggerIssues.length > 0) {
801
+ printCliApiIssues(triggerIssues, ' Trigger issues:');
802
+ }
681
803
  console.log();
682
804
  (0, ui_1.divider)();
683
805
  if (data.valid) {
@@ -710,13 +832,10 @@ async function activateAgentCommand(idOrName, opts) {
710
832
  const resp = await (0, api_1.post)(`/api/agent/${agent.id}/activate`);
711
833
  if (!resp.ok) {
712
834
  spinner?.fail();
713
- const errorMsg = resp.data?.error || resp.data?.message || 'Unknown error';
714
- if (jsonMode) {
715
- (0, tty_1.outputJson)({ success: false, error: errorMsg });
716
- }
717
- else {
718
- (0, ui_1.error)(`Failed to activate: ${errorMsg}`);
719
- }
835
+ const errorMsg = handleCliApiFailure(resp.data, {
836
+ jsonMode,
837
+ prefix: 'Failed to activate: ',
838
+ });
720
839
  (0, memory_1.appendLearning)({ category: 'error', message: `Activate failed: ${errorMsg}`, command: 'agent activate', agentId: agent.id });
721
840
  return;
722
841
  }
@@ -755,13 +874,10 @@ async function deactivateAgentCommand(idOrName, opts) {
755
874
  const resp = await (0, api_1.post)(`/api/agent/${agent.id}/deactivate`);
756
875
  if (!resp.ok) {
757
876
  spinner?.fail();
758
- const errorMsg = resp.data?.error || resp.data?.message || 'Unknown error';
759
- if (jsonMode) {
760
- (0, tty_1.outputJson)({ success: false, error: errorMsg });
761
- }
762
- else {
763
- (0, ui_1.error)(`Failed to deactivate: ${errorMsg}`);
764
- }
877
+ const errorMsg = handleCliApiFailure(resp.data, {
878
+ jsonMode,
879
+ prefix: 'Failed to deactivate: ',
880
+ });
765
881
  (0, memory_1.appendLearning)({ category: 'error', message: `Deactivate failed: ${errorMsg}`, command: 'agent deactivate', agentId: agent.id });
766
882
  return;
767
883
  }
@@ -841,13 +957,10 @@ async function executeAgentCommand(idOrName, trigger, opts) {
841
957
  });
842
958
  if (!resp.ok) {
843
959
  spinner?.fail();
844
- const errorMsg = resp.data?.error || resp.data?.message || 'Unknown error';
845
- if (jsonMode) {
846
- (0, tty_1.outputJson)({ success: false, error: errorMsg });
847
- }
848
- else {
849
- (0, ui_1.error)(`Execution failed: ${errorMsg}`);
850
- }
960
+ const errorMsg = handleCliApiFailure(resp.data, {
961
+ jsonMode,
962
+ prefix: 'Execution failed: ',
963
+ });
851
964
  (0, memory_1.appendLearning)({ category: 'error', message: `Execution failed: ${errorMsg}`, command: 'agent execute', agentId: agent.id });
852
965
  return;
853
966
  }
@@ -997,4 +1110,514 @@ async function deleteAgentCommand(idOrName, opts) {
997
1110
  (0, ui_1.success)(`Agent ${agent.id.slice(0, 8)}... deleted.`);
998
1111
  }
999
1112
  }
1113
+ // ============================================================================
1114
+ // Git Version Control Commands
1115
+ // ============================================================================
1116
+ async function historyCommand(idOrName, opts) {
1117
+ if (!(0, ui_1.requireAuth)())
1118
+ return;
1119
+ const jsonMode = opts?.json || false;
1120
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1121
+ if (!agent)
1122
+ return;
1123
+ const limit = opts?.limit || '20';
1124
+ const spinner = (0, tty_1.createSpinner)('Loading commit history...', jsonMode);
1125
+ spinner?.start();
1126
+ const resp = await (0, api_1.get)(`/api/agent/${agent.id}/history?limit=${limit}`);
1127
+ if (!resp.ok) {
1128
+ spinner?.fail();
1129
+ const msg = resp.data?.message || resp.data?.error || 'Failed to load history';
1130
+ if (jsonMode) {
1131
+ (0, tty_1.outputJson)({ success: false, error: msg });
1132
+ }
1133
+ else {
1134
+ (0, ui_1.error)(msg);
1135
+ }
1136
+ return;
1137
+ }
1138
+ spinner?.stop();
1139
+ const commits = resp.data.commits || [];
1140
+ if (jsonMode) {
1141
+ (0, tty_1.outputJson)({ success: true, data: { commits, count: commits.length, agentId: agent.id } });
1142
+ return;
1143
+ }
1144
+ if (commits.length === 0) {
1145
+ (0, ui_1.header)('Commit History');
1146
+ console.log();
1147
+ (0, ui_1.info)('No commits found for this agent.');
1148
+ return;
1149
+ }
1150
+ (0, ui_1.header)(`Commit History — ${agent.title || 'Untitled'}`);
1151
+ console.log();
1152
+ for (const commit of commits) {
1153
+ const sha = ui_1.colors.highlight(commit.sha.substring(0, 7));
1154
+ const date = ui_1.colors.muted(new Date(commit.timestamp || commit.createdAt).toLocaleString());
1155
+ const msg = commit.message || 'No message';
1156
+ console.log(` ${sha} ${msg} ${date}`);
1157
+ }
1158
+ console.log();
1159
+ console.log(ui_1.colors.muted(` ${commits.length} commit(s)`));
1160
+ }
1161
+ async function diffCommand(idOrName, sha, opts) {
1162
+ if (!(0, ui_1.requireAuth)())
1163
+ return;
1164
+ const jsonMode = opts?.json || false;
1165
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1166
+ if (!agent)
1167
+ return;
1168
+ // If no sha, pick from history
1169
+ if (!sha) {
1170
+ if (!(0, tty_1.shouldUseInteractive)()) {
1171
+ const msg = 'Commit SHA is required in non-interactive mode.';
1172
+ if (jsonMode) {
1173
+ (0, tty_1.outputJson)({ success: false, error: msg });
1174
+ }
1175
+ else {
1176
+ (0, ui_1.error)(msg);
1177
+ }
1178
+ return;
1179
+ }
1180
+ const histResp = await (0, api_1.get)(`/api/agent/${agent.id}/history?limit=20`);
1181
+ if (!histResp.ok || !histResp.data?.commits?.length) {
1182
+ (0, ui_1.error)('No commits found to diff.');
1183
+ return;
1184
+ }
1185
+ const { selected } = await inquirer_1.default.prompt([
1186
+ {
1187
+ type: 'list',
1188
+ name: 'selected',
1189
+ message: 'Select a commit to view diff:',
1190
+ choices: histResp.data.commits.map((c) => ({
1191
+ name: `${c.sha.substring(0, 7)} ${c.message || 'No message'} ${ui_1.colors.muted(new Date(c.timestamp || '').toLocaleString())}`,
1192
+ value: c.sha,
1193
+ })),
1194
+ },
1195
+ ]);
1196
+ sha = selected;
1197
+ }
1198
+ const spinner = (0, tty_1.createSpinner)('Loading diff...', jsonMode);
1199
+ spinner?.start();
1200
+ const resp = await (0, api_1.get)(`/api/agent/${agent.id}/diff/${sha}`);
1201
+ if (!resp.ok) {
1202
+ spinner?.fail();
1203
+ const msg = resp.data?.message || resp.data?.error || 'Failed to load diff';
1204
+ if (jsonMode) {
1205
+ (0, tty_1.outputJson)({ success: false, error: msg });
1206
+ }
1207
+ else {
1208
+ (0, ui_1.error)(msg);
1209
+ }
1210
+ return;
1211
+ }
1212
+ spinner?.stop();
1213
+ if (jsonMode) {
1214
+ (0, tty_1.outputJson)({ success: true, data: { sha, patch: resp.data.patch, additions: resp.data.additions, deletions: resp.data.deletions, agentId: agent.id } });
1215
+ return;
1216
+ }
1217
+ (0, ui_1.header)(`Diff — ${sha.substring(0, 7)}`);
1218
+ console.log();
1219
+ const patch = resp.data.patch || '';
1220
+ if (!patch) {
1221
+ (0, ui_1.info)('No changes in this commit.');
1222
+ return;
1223
+ }
1224
+ if (resp.data.additions !== undefined || resp.data.deletions !== undefined) {
1225
+ console.log(ui_1.colors.muted(` ${chalk_1.default.green(`+${resp.data.additions || 0}`)} ${chalk_1.default.red(`-${resp.data.deletions || 0}`)}`));
1226
+ console.log();
1227
+ }
1228
+ // Colorize diff output
1229
+ for (const line of patch.split('\n')) {
1230
+ if (line.startsWith('+++') || line.startsWith('---')) {
1231
+ console.log(chalk_1.default.bold(line));
1232
+ }
1233
+ else if (line.startsWith('+')) {
1234
+ console.log(chalk_1.default.green(line));
1235
+ }
1236
+ else if (line.startsWith('-')) {
1237
+ console.log(chalk_1.default.red(line));
1238
+ }
1239
+ else if (line.startsWith('@@')) {
1240
+ console.log(chalk_1.default.cyan(line));
1241
+ }
1242
+ else {
1243
+ console.log(line);
1244
+ }
1245
+ }
1246
+ }
1247
+ async function versionCommand(idOrName, sha, opts) {
1248
+ if (!(0, ui_1.requireAuth)())
1249
+ return;
1250
+ const jsonMode = opts?.json || false;
1251
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1252
+ if (!agent)
1253
+ return;
1254
+ // If no sha, pick from history
1255
+ if (!sha) {
1256
+ if (!(0, tty_1.shouldUseInteractive)()) {
1257
+ const msg = 'Commit SHA is required in non-interactive mode.';
1258
+ if (jsonMode) {
1259
+ (0, tty_1.outputJson)({ success: false, error: msg });
1260
+ }
1261
+ else {
1262
+ (0, ui_1.error)(msg);
1263
+ }
1264
+ return;
1265
+ }
1266
+ const histResp = await (0, api_1.get)(`/api/agent/${agent.id}/history?limit=20`);
1267
+ if (!histResp.ok || !histResp.data?.commits?.length) {
1268
+ (0, ui_1.error)('No commits found.');
1269
+ return;
1270
+ }
1271
+ const { selected } = await inquirer_1.default.prompt([
1272
+ {
1273
+ type: 'list',
1274
+ name: 'selected',
1275
+ message: 'Select a commit to view source:',
1276
+ choices: histResp.data.commits.map((c) => ({
1277
+ name: `${c.sha.substring(0, 7)} ${c.message || 'No message'} ${ui_1.colors.muted(new Date(c.timestamp || '').toLocaleString())}`,
1278
+ value: c.sha,
1279
+ })),
1280
+ },
1281
+ ]);
1282
+ sha = selected;
1283
+ }
1284
+ const spinner = (0, tty_1.createSpinner)('Loading version...', jsonMode);
1285
+ spinner?.start();
1286
+ const resp = await (0, api_1.get)(`/api/agent/${agent.id}/version/${sha}`);
1287
+ if (!resp.ok) {
1288
+ spinner?.fail();
1289
+ const msg = resp.data?.message || resp.data?.error || 'Failed to load version';
1290
+ if (jsonMode) {
1291
+ (0, tty_1.outputJson)({ success: false, error: msg });
1292
+ }
1293
+ else {
1294
+ (0, ui_1.error)(msg);
1295
+ }
1296
+ return;
1297
+ }
1298
+ spinner?.stop();
1299
+ const source = resp.data.source || '';
1300
+ if (jsonMode) {
1301
+ (0, tty_1.outputJson)({ success: true, data: { sha, source, agentId: agent.id } });
1302
+ return;
1303
+ }
1304
+ (0, ui_1.header)(`Version — ${sha.substring(0, 7)}`);
1305
+ console.log();
1306
+ if (!source) {
1307
+ (0, ui_1.info)('No source code at this commit.');
1308
+ return;
1309
+ }
1310
+ const lines = source.split('\n');
1311
+ for (let i = 0; i < lines.length; i++) {
1312
+ const lineNum = ui_1.colors.muted(String(i + 1).padStart(4));
1313
+ console.log(`${lineNum} ${lines[i]}`);
1314
+ }
1315
+ console.log();
1316
+ console.log(ui_1.colors.muted(` ${lines.length} lines`));
1317
+ }
1318
+ async function checkoutCommand(idOrName, sha, opts) {
1319
+ if (!(0, ui_1.requireAuth)())
1320
+ return;
1321
+ const jsonMode = opts?.json || false;
1322
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1323
+ if (!agent)
1324
+ return;
1325
+ // If no sha, pick from history
1326
+ if (!sha) {
1327
+ if (!(0, tty_1.shouldUseInteractive)()) {
1328
+ const msg = 'Commit SHA is required in non-interactive mode.';
1329
+ if (jsonMode) {
1330
+ (0, tty_1.outputJson)({ success: false, error: msg });
1331
+ }
1332
+ else {
1333
+ (0, ui_1.error)(msg);
1334
+ }
1335
+ return;
1336
+ }
1337
+ const histResp = await (0, api_1.get)(`/api/agent/${agent.id}/history?limit=20`);
1338
+ if (!histResp.ok || !histResp.data?.commits?.length) {
1339
+ (0, ui_1.error)('No commits found.');
1340
+ return;
1341
+ }
1342
+ const { selected } = await inquirer_1.default.prompt([
1343
+ {
1344
+ type: 'list',
1345
+ name: 'selected',
1346
+ message: 'Select a commit to revert to:',
1347
+ choices: histResp.data.commits.map((c) => ({
1348
+ name: `${c.sha.substring(0, 7)} ${c.message || 'No message'} ${ui_1.colors.muted(new Date(c.timestamp || '').toLocaleString())}`,
1349
+ value: c.sha,
1350
+ })),
1351
+ },
1352
+ ]);
1353
+ sha = selected;
1354
+ }
1355
+ // Confirm destructive action
1356
+ if (!jsonMode && (0, tty_1.shouldUseInteractive)()) {
1357
+ const { confirm } = await inquirer_1.default.prompt([
1358
+ {
1359
+ type: 'confirm',
1360
+ name: 'confirm',
1361
+ message: `Revert "${agent.title || 'Untitled'}" to commit ${sha.substring(0, 7)}? This will overwrite the current code.`,
1362
+ default: false,
1363
+ },
1364
+ ]);
1365
+ if (!confirm) {
1366
+ (0, ui_1.info)('Cancelled.');
1367
+ return;
1368
+ }
1369
+ }
1370
+ const spinner = (0, tty_1.createSpinner)('Reverting agent...', jsonMode);
1371
+ spinner?.start();
1372
+ const resp = await (0, api_1.post)(`/api/agent/${agent.id}/checkout/${sha}`);
1373
+ if (!resp.ok) {
1374
+ spinner?.fail();
1375
+ const msg = resp.data?.message || resp.data?.error || 'Failed to checkout';
1376
+ if (jsonMode) {
1377
+ (0, tty_1.outputJson)({ success: false, error: msg });
1378
+ }
1379
+ else {
1380
+ (0, ui_1.error)(msg);
1381
+ }
1382
+ return;
1383
+ }
1384
+ spinner?.succeed();
1385
+ if (jsonMode) {
1386
+ (0, tty_1.outputJson)({ success: true, data: { agentId: agent.id, commitSha: resp.data.commitSha, revertedTo: sha } });
1387
+ }
1388
+ else {
1389
+ (0, ui_1.success)(`Agent reverted to ${sha.substring(0, 7)}. New commit: ${resp.data.commitSha?.substring(0, 7) || 'N/A'}`);
1390
+ }
1391
+ }
1392
+ async function forkCommand(idOrName, opts) {
1393
+ if (!(0, ui_1.requireAuth)())
1394
+ return;
1395
+ const jsonMode = opts?.json || false;
1396
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1397
+ if (!agent)
1398
+ return;
1399
+ const spinner = (0, tty_1.createSpinner)('Forking agent...', jsonMode);
1400
+ spinner?.start();
1401
+ const resp = await (0, api_1.post)(`/api/agent/${agent.id}/fork`);
1402
+ if (!resp.ok) {
1403
+ spinner?.fail();
1404
+ const msg = resp.data?.message || resp.data?.error || 'Failed to fork agent';
1405
+ if (jsonMode) {
1406
+ (0, tty_1.outputJson)({ success: false, error: msg });
1407
+ }
1408
+ else {
1409
+ (0, ui_1.error)(msg);
1410
+ }
1411
+ return;
1412
+ }
1413
+ spinner?.succeed();
1414
+ const forkData = resp.data;
1415
+ if (jsonMode) {
1416
+ (0, tty_1.outputJson)({ success: true, data: { fork: forkData, upstreamId: agent.id } });
1417
+ return;
1418
+ }
1419
+ (0, ui_1.success)('Agent forked!');
1420
+ console.log();
1421
+ (0, ui_1.label)('Fork ID', forkData.agent?.id || forkData.id || 'N/A');
1422
+ (0, ui_1.label)('Title', forkData.agent?.title || 'N/A');
1423
+ (0, ui_1.label)('Upstream', `${agent.id.slice(0, 8)}... (${agent.title || 'Untitled'})`);
1424
+ if (forkData.commitSha) {
1425
+ (0, ui_1.label)('Commit', forkData.commitSha.substring(0, 7));
1426
+ }
1427
+ console.log();
1428
+ (0, ui_1.divider)();
1429
+ }
1430
+ async function upstreamCommand(idOrName, opts) {
1431
+ if (!(0, ui_1.requireAuth)())
1432
+ return;
1433
+ const jsonMode = opts?.json || false;
1434
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1435
+ if (!agent)
1436
+ return;
1437
+ const spinner = (0, tty_1.createSpinner)('Checking upstream...', jsonMode);
1438
+ spinner?.start();
1439
+ const resp = await (0, api_1.get)(`/api/agent/${agent.id}/upstream`);
1440
+ if (!resp.ok) {
1441
+ spinner?.fail();
1442
+ const msg = resp.data?.message || resp.data?.error || 'Failed to check upstream';
1443
+ if (jsonMode) {
1444
+ (0, tty_1.outputJson)({ success: false, error: msg });
1445
+ }
1446
+ else {
1447
+ (0, ui_1.error)(msg);
1448
+ }
1449
+ return;
1450
+ }
1451
+ spinner?.stop();
1452
+ const status = resp.data;
1453
+ if (jsonMode) {
1454
+ (0, tty_1.outputJson)({ success: true, data: { agentId: agent.id, ...status } });
1455
+ return;
1456
+ }
1457
+ (0, ui_1.header)(`Upstream Status — ${agent.title || 'Untitled'}`);
1458
+ console.log();
1459
+ const commits = status.newCommits || status.commits || [];
1460
+ if (status.hasUpdates) {
1461
+ (0, ui_1.warn)('Upstream has new changes available.');
1462
+ (0, ui_1.label)('New commits', String(commits.length));
1463
+ if (commits.length) {
1464
+ console.log();
1465
+ console.log(ui_1.colors.muted(' Recent upstream commits:'));
1466
+ for (const c of commits.slice(0, 5)) {
1467
+ console.log(` ${ui_1.colors.highlight(c.sha.substring(0, 7))} ${c.message || 'No message'}`);
1468
+ }
1469
+ }
1470
+ console.log();
1471
+ (0, ui_1.info)(`Pull changes with: suzi agents pull ${agent.id}`);
1472
+ }
1473
+ else {
1474
+ (0, ui_1.success)('Up to date with upstream.');
1475
+ }
1476
+ }
1477
+ async function pullCommand(idOrName, opts) {
1478
+ if (!(0, ui_1.requireAuth)())
1479
+ return;
1480
+ const jsonMode = opts?.json || false;
1481
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1482
+ if (!agent)
1483
+ return;
1484
+ const spinner = (0, tty_1.createSpinner)('Pulling upstream changes...', jsonMode);
1485
+ spinner?.start();
1486
+ const resp = await (0, api_1.post)(`/api/agent/${agent.id}/pull`);
1487
+ if (!resp.ok) {
1488
+ spinner?.fail();
1489
+ const msg = resp.data?.message || resp.data?.error || 'Failed to pull upstream';
1490
+ if (jsonMode) {
1491
+ (0, tty_1.outputJson)({ success: false, error: msg });
1492
+ }
1493
+ else {
1494
+ (0, ui_1.error)(msg);
1495
+ }
1496
+ return;
1497
+ }
1498
+ spinner?.stop();
1499
+ const result = resp.data;
1500
+ if (jsonMode) {
1501
+ (0, tty_1.outputJson)({ success: true, data: { agentId: agent.id, ...result } });
1502
+ return;
1503
+ }
1504
+ if (result.status === 'up-to-date') {
1505
+ (0, ui_1.success)('Already up to date.');
1506
+ return;
1507
+ }
1508
+ if (result.status === 'clean') {
1509
+ (0, ui_1.success)('Merged upstream changes cleanly!');
1510
+ if (result.commitSha) {
1511
+ (0, ui_1.label)('Commit', result.commitSha.substring(0, 7));
1512
+ }
1513
+ return;
1514
+ }
1515
+ if (result.status === 'conflict') {
1516
+ (0, ui_1.warn)('Merge conflicts detected!');
1517
+ console.log();
1518
+ const conflicts = result.conflicts || [];
1519
+ console.log(chalk_1.default.bold(` ${conflicts.length} conflict region(s):`));
1520
+ console.log();
1521
+ for (let i = 0; i < conflicts.length; i++) {
1522
+ const c = conflicts[i];
1523
+ console.log(chalk_1.default.bold(` Conflict ${i + 1}:`));
1524
+ if (c.ours) {
1525
+ console.log(chalk_1.default.green(' <<<< Your version:'));
1526
+ for (const line of c.ours.split('\n')) {
1527
+ console.log(chalk_1.default.green(` ${line}`));
1528
+ }
1529
+ }
1530
+ if (c.theirs) {
1531
+ console.log(chalk_1.default.red(' >>>> Upstream version:'));
1532
+ for (const line of c.theirs.split('\n')) {
1533
+ console.log(chalk_1.default.red(` ${line}`));
1534
+ }
1535
+ }
1536
+ console.log();
1537
+ }
1538
+ if (result.merged) {
1539
+ console.log(ui_1.colors.muted(' Merged source with conflict markers:'));
1540
+ console.log(ui_1.colors.muted(' ─'.repeat(40)));
1541
+ const lines = result.merged.split('\n');
1542
+ for (let i = 0; i < lines.length; i++) {
1543
+ const line = lines[i];
1544
+ if (line.startsWith('<<<<<<<')) {
1545
+ console.log(chalk_1.default.green(` ${line}`));
1546
+ }
1547
+ else if (line.startsWith('=======')) {
1548
+ console.log(chalk_1.default.yellow(` ${line}`));
1549
+ }
1550
+ else if (line.startsWith('>>>>>>>')) {
1551
+ console.log(chalk_1.default.red(` ${line}`));
1552
+ }
1553
+ else {
1554
+ console.log(` ${line}`);
1555
+ }
1556
+ }
1557
+ }
1558
+ console.log();
1559
+ (0, ui_1.info)('Resolve conflicts and use the API to submit resolved source.');
1560
+ if (result.upstreamHead) {
1561
+ (0, ui_1.label)('Upstream HEAD', result.upstreamHead.substring(0, 7));
1562
+ }
1563
+ return;
1564
+ }
1565
+ // Unknown status
1566
+ (0, ui_1.info)(`Pull result: ${result.status || 'unknown'}`);
1567
+ }
1568
+ async function listForksCommand(idOrName, opts) {
1569
+ if (!(0, ui_1.requireAuth)())
1570
+ return;
1571
+ const jsonMode = opts?.json || false;
1572
+ const agent = await (0, agent_picker_1.fetchAndPickAgent)(idOrName, jsonMode);
1573
+ if (!agent)
1574
+ return;
1575
+ const spinner = (0, tty_1.createSpinner)('Loading forks...', jsonMode);
1576
+ spinner?.start();
1577
+ const resp = await (0, api_1.get)(`/api/agent/${agent.id}/forks`);
1578
+ if (!resp.ok) {
1579
+ spinner?.fail();
1580
+ const msg = resp.data?.message || resp.data?.error || 'Failed to load forks';
1581
+ if (jsonMode) {
1582
+ (0, tty_1.outputJson)({ success: false, error: msg });
1583
+ }
1584
+ else {
1585
+ (0, ui_1.error)(msg);
1586
+ }
1587
+ return;
1588
+ }
1589
+ spinner?.stop();
1590
+ const forks = resp.data.forks || [];
1591
+ if (jsonMode) {
1592
+ (0, tty_1.outputJson)({ success: true, data: { forks, count: forks.length, agentId: agent.id } });
1593
+ return;
1594
+ }
1595
+ if (forks.length === 0) {
1596
+ (0, ui_1.header)('Forks');
1597
+ console.log();
1598
+ (0, ui_1.info)('No forks found for this agent.');
1599
+ return;
1600
+ }
1601
+ (0, ui_1.header)(`Forks of ${agent.title || 'Untitled'} (${forks.length})`);
1602
+ console.log();
1603
+ const table = new cli_table3_1.default({
1604
+ head: [
1605
+ chalk_1.default.gray('#'),
1606
+ chalk_1.default.gray('Agent ID'),
1607
+ chalk_1.default.gray('Last Synced'),
1608
+ chalk_1.default.gray('Created'),
1609
+ ],
1610
+ style: { head: [], border: ['gray'] },
1611
+ });
1612
+ for (let i = 0; i < forks.length; i++) {
1613
+ const f = forks[i];
1614
+ table.push([
1615
+ ui_1.colors.muted(String(i + 1)),
1616
+ f.childAgentId || f.agentId || 'N/A',
1617
+ f.lastSyncedSha ? f.lastSyncedSha.substring(0, 7) : ui_1.colors.muted('N/A'),
1618
+ f.createdAt ? ui_1.colors.muted(new Date(f.createdAt).toLocaleDateString()) : ui_1.colors.muted('N/A'),
1619
+ ]);
1620
+ }
1621
+ console.log(table.toString());
1622
+ }
1000
1623
  //# sourceMappingURL=agent.js.map