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.
- package/dist/commands/agent.d.ts +25 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +670 -47
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +2 -6
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +1 -11
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/run.js +8 -8
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/transactions.d.ts.map +1 -1
- package/dist/commands/transactions.js +49 -30
- package/dist/commands/transactions.js.map +1 -1
- package/dist/lib/api-errors.d.ts +17 -0
- package/dist/lib/api-errors.d.ts.map +1 -0
- package/dist/lib/api-errors.js +84 -0
- package/dist/lib/api-errors.js.map +1 -0
- package/dist/lib/api-errors.test.d.ts +2 -0
- package/dist/lib/api-errors.test.d.ts.map +1 -0
- package/dist/lib/api-errors.test.js +51 -0
- package/dist/lib/api-errors.test.js.map +1 -0
- package/package.json +1 -1
- package/dist/lib/suzi-guide.d.ts +0 -2
- package/dist/lib/suzi-guide.d.ts.map +0 -1
- package/dist/lib/suzi-guide.js +0 -682
- package/dist/lib/suzi-guide.js.map +0 -1
package/dist/commands/agent.js
CHANGED
|
@@ -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
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
|
544
|
+
const parsedError = (0, api_errors_1.parseCliApiError)(activateResp.data);
|
|
545
|
+
const activateError = parsedError.summary;
|
|
442
546
|
if (jsonMode) {
|
|
443
|
-
(0, tty_1.outputJson)({
|
|
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
|
|
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
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
|
714
|
-
|
|
715
|
-
|
|
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
|
|
759
|
-
|
|
760
|
-
|
|
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
|
|
845
|
-
|
|
846
|
-
|
|
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
|