@thegitai/cli 1.0.0-beta.17 → 1.0.0-beta.18
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/src/agent-mode.js
CHANGED
|
@@ -118,9 +118,7 @@ export function buildAgentModeToolBlockedResult(mode, call) {
|
|
|
118
118
|
}
|
|
119
119
|
if (call.name !== 'run_command')
|
|
120
120
|
return null;
|
|
121
|
-
if (call.args?.background === true
|
|
122
|
-
call.args?.run_in_background === true ||
|
|
123
|
-
call.args?.runInBackground === true) {
|
|
121
|
+
if (call.args?.background === true) {
|
|
124
122
|
return buildPlanModeToolBlockedResult(call.name, PLAN_MODE_RUN_COMMAND_ACTION);
|
|
125
123
|
}
|
|
126
124
|
const command = String(call.args?.command ?? call.args?.cmd ?? '');
|
|
@@ -13,7 +13,7 @@ export async function runShellCommand(context, args) {
|
|
|
13
13
|
if (!command) {
|
|
14
14
|
return { ok: false, error: 'command is required' };
|
|
15
15
|
}
|
|
16
|
-
const runInBackground = args.background === true
|
|
16
|
+
const runInBackground = args.background === true;
|
|
17
17
|
const hasTimeout = typeof args.timeout_ms === 'number' && args.timeout_ms > 0;
|
|
18
18
|
const repoHint = buildNestedGitHint(rootDir, command);
|
|
19
19
|
const blockedReason = getBlockedCommandReason(command, hasTimeout || runInBackground, rootDir);
|
|
@@ -40,7 +40,7 @@ export async function runShellCommand(context, args) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
const approved = await confirmCommand(runInBackground
|
|
43
|
-
? `${command}\n\nRuns as a managed background job until it exits or is
|
|
43
|
+
? `${command}\n\nRuns as a managed background job until it exits or is killed.`
|
|
44
44
|
: command);
|
|
45
45
|
if (!approved) {
|
|
46
46
|
if (!isTuiMode())
|
|
@@ -102,6 +102,7 @@ async function runBackgroundCommand(context, command, timeoutMs, repoHint) {
|
|
|
102
102
|
const { rootDir, projectIndex, onStatus } = context;
|
|
103
103
|
const started = await startBackgroundJob(command, rootDir, {
|
|
104
104
|
startupWaitMs: typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : undefined,
|
|
105
|
+
sessionId: context.sessionId,
|
|
105
106
|
});
|
|
106
107
|
if (!started.ok || !started.snapshot) {
|
|
107
108
|
if (!isTuiMode())
|
|
@@ -287,6 +287,17 @@ function footerTransientStatus(status) {
|
|
|
287
287
|
}
|
|
288
288
|
return null;
|
|
289
289
|
}
|
|
290
|
+
function backgroundJobIndicatorSpans(state) {
|
|
291
|
+
const runningCount = (state.backgroundJobs ?? []).filter((job) => job.status === 'running').length;
|
|
292
|
+
if (runningCount === 0)
|
|
293
|
+
return [];
|
|
294
|
+
const noun = runningCount === 1 ? 'shell' : 'shells';
|
|
295
|
+
return [
|
|
296
|
+
span(' ', { color: 'gray', dim: true }),
|
|
297
|
+
span(`● ${runningCount} ${noun} running`, { color: 'green', bold: true }),
|
|
298
|
+
span(' · /jobs', { color: 'gray', dim: true }),
|
|
299
|
+
];
|
|
300
|
+
}
|
|
290
301
|
function composerFooterLines(state) {
|
|
291
302
|
const visibleSessionId = formatPromptSessionIdLabel(state.showSessionId, state.sessionId);
|
|
292
303
|
const transientStatus = footerTransientStatus(state.status);
|
|
@@ -305,7 +316,7 @@ function composerFooterLines(state) {
|
|
|
305
316
|
]
|
|
306
317
|
: []), ...(visibleSessionId
|
|
307
318
|
? [span(` ${visibleSessionId}`, { color: 'gray', dim: true })]
|
|
308
|
-
: [])),
|
|
319
|
+
: []), ...backgroundJobIndicatorSpans(state)),
|
|
309
320
|
plainLine(''),
|
|
310
321
|
plainLine(formatModelLabel(state.currentModelId, state.serverModels), {
|
|
311
322
|
color: 'cyan',
|
|
@@ -352,20 +363,22 @@ export function formatJobElapsed(elapsedMs) {
|
|
|
352
363
|
}
|
|
353
364
|
return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`;
|
|
354
365
|
}
|
|
355
|
-
function
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
+
function backgroundJobPreviewLines(job, maxTailLines) {
|
|
367
|
+
const lines = [];
|
|
368
|
+
const first = String(job.firstOutputLine ?? '').trim();
|
|
369
|
+
if (first) {
|
|
370
|
+
lines.push(first);
|
|
371
|
+
}
|
|
372
|
+
for (const outputLine of (job.tailLines ?? []).slice(-maxTailLines)) {
|
|
373
|
+
const trimmed = String(outputLine ?? '').trim();
|
|
374
|
+
if (!trimmed)
|
|
375
|
+
continue;
|
|
376
|
+
if (lines.length === 1 && trimmed === first)
|
|
377
|
+
continue;
|
|
378
|
+
lines.push(trimmed);
|
|
379
|
+
}
|
|
380
|
+
return lines;
|
|
366
381
|
}
|
|
367
|
-
const JOB_LIVE_TAIL_LINES = 8;
|
|
368
|
-
const JOB_FINISHED_TAIL_LINES = 3;
|
|
369
382
|
function jobStatusDescriptor(job, nowMs) {
|
|
370
383
|
const ran = formatJobElapsed((job.endedAt ?? (nowMs > 0 ? nowMs : Date.now())) - job.startedAt);
|
|
371
384
|
if (job.status === 'running') {
|
|
@@ -381,23 +394,6 @@ function jobStatusDescriptor(job, nowMs) {
|
|
|
381
394
|
? { glyph: '✓', color: 'green', text: `exited (0) · ran ${ran}` }
|
|
382
395
|
: { glyph: '✖', color: 'red', text: `exited (${job.exitCode ?? 1}) · ran ${ran}` };
|
|
383
396
|
}
|
|
384
|
-
function renderBackgroundJobEntryLines(entry, state, width, nowMs) {
|
|
385
|
-
const job = (state.backgroundJobs ?? []).find((candidate) => candidate.id === entry.jobId);
|
|
386
|
-
if (!job) {
|
|
387
|
-
return renderTranscriptEntryLines({ ...entry, jobId: undefined }, width);
|
|
388
|
-
}
|
|
389
|
-
const { glyph, color, text } = jobStatusDescriptor(job, nowMs);
|
|
390
|
-
const header = line(span(`${glyph} `, { color }), span(`${job.id} · ${truncate(job.command, Math.max(12, width - 24))}`, {
|
|
391
|
-
color,
|
|
392
|
-
bold: true,
|
|
393
|
-
}), span(` · ${text}`, { color: 'gray' }));
|
|
394
|
-
const tailBudget = job.status === 'running' ? JOB_LIVE_TAIL_LINES : JOB_FINISHED_TAIL_LINES;
|
|
395
|
-
const tail = (job.tailLines ?? []).slice(-tailBudget).map((tailLine) => line(span('│ ', { color: 'gray', dim: true }), span(truncate(tailLine, Math.max(8, width - 4)), {
|
|
396
|
-
color: 'gray',
|
|
397
|
-
dim: true,
|
|
398
|
-
})));
|
|
399
|
-
return [header, ...tail];
|
|
400
|
-
}
|
|
401
397
|
function buildJobsPickerLines(state, width, nowMs) {
|
|
402
398
|
const jobs = state.backgroundJobs ?? [];
|
|
403
399
|
const lines = [
|
|
@@ -408,13 +404,30 @@ function buildJobsPickerLines(state, width, nowMs) {
|
|
|
408
404
|
}
|
|
409
405
|
for (const [index, job] of jobs.entries()) {
|
|
410
406
|
const selected = index === state.jobsPickerIndex;
|
|
407
|
+
const expanded = state.jobsPickerExpandedId === job.id;
|
|
411
408
|
const { glyph, color, text } = jobStatusDescriptor(job, nowMs);
|
|
412
409
|
lines.push(line(span(selected ? '› ' : ' ', { color: selected ? 'cyan' : 'gray' }), span(`${glyph} `, { color }), span(job.id.padEnd(6), { color: selected ? 'cyan' : undefined, bold: selected }), span(` ${truncate(job.command, Math.max(12, width - 34)).padEnd(Math.max(12, Math.min(40, width - 34)))}`, {
|
|
413
410
|
color: selected ? 'cyan' : undefined,
|
|
414
411
|
bold: selected,
|
|
415
412
|
}), span(` ${text}`, { color: 'gray' })));
|
|
413
|
+
if (expanded) {
|
|
414
|
+
const detailLines = (job.detailLines ?? []).length
|
|
415
|
+
? job.detailLines ?? []
|
|
416
|
+
: backgroundJobPreviewLines(job, 3);
|
|
417
|
+
if (detailLines.length === 0) {
|
|
418
|
+
lines.push(plainLine(' (no output captured)', { color: 'gray' }));
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
for (const outputLine of detailLines) {
|
|
422
|
+
lines.push(line(span(' │ ', { color: 'gray', dim: true }), span(truncate(outputLine, Math.max(8, width - 8)), {
|
|
423
|
+
color: 'gray',
|
|
424
|
+
dim: true,
|
|
425
|
+
})));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
416
429
|
}
|
|
417
|
-
lines.push(plainLine('↑/↓ move enter
|
|
430
|
+
lines.push(plainLine('↑/↓ move enter expand/collapse k kill esc close', {
|
|
418
431
|
color: 'gray',
|
|
419
432
|
}));
|
|
420
433
|
return lines;
|
|
@@ -751,9 +764,7 @@ function sliceTranscriptLines(lines, maxLines, scrollOffset) {
|
|
|
751
764
|
export function buildTuiFrame(state, cols, rows, spinnerFrame, elapsedSeconds, nowMs = 0) {
|
|
752
765
|
const contentWidth = Math.max(20, Math.floor(cols * 0.95) - 2);
|
|
753
766
|
const gutter = Math.max(Math.floor((cols - contentWidth) / 2), 0);
|
|
754
|
-
const transcriptBlocks = state.transcript.map((entry) => entry
|
|
755
|
-
? renderBackgroundJobEntryLines(entry, state, contentWidth, nowMs)
|
|
756
|
-
: renderTranscriptEntryLines(entry, contentWidth));
|
|
767
|
+
const transcriptBlocks = state.transcript.map((entry) => renderTranscriptEntryLines(entry, contentWidth));
|
|
757
768
|
const transcriptLines = [];
|
|
758
769
|
transcriptBlocks.forEach((block, index) => {
|
|
759
770
|
if (index > 0) {
|
|
@@ -768,10 +779,6 @@ export function buildTuiFrame(state, cols, rows, spinnerFrame, elapsedSeconds, n
|
|
|
768
779
|
}
|
|
769
780
|
const overlayActive = Boolean(state.approvalPrompt || state.sudoPrompt);
|
|
770
781
|
if (!state.resumePickerOpen && !state.modelPickerOpen && !state.jobsPickerOpen && !overlayActive) {
|
|
771
|
-
const jobsLine = buildBackgroundJobsLine(state, contentWidth, nowMs);
|
|
772
|
-
if (jobsLine) {
|
|
773
|
-
sections.push({ kind: 'busyFooter', lines: [jobsLine, plainLine('')] });
|
|
774
|
-
}
|
|
775
782
|
const composerLines = [];
|
|
776
783
|
if (state.queuedMessage) {
|
|
777
784
|
const preview = truncate(state.queuedMessage.body.trim().replace(/\s+/g, ' '), 60);
|
|
@@ -216,6 +216,7 @@ export function handleShellKeyEvent(store, handlers, event) {
|
|
|
216
216
|
if (key.escape) {
|
|
217
217
|
store.update((current) => ({
|
|
218
218
|
...current,
|
|
219
|
+
jobsPickerExpandedId: null,
|
|
219
220
|
jobsPickerOpen: false,
|
|
220
221
|
status: 'Ready',
|
|
221
222
|
}));
|
|
@@ -227,6 +228,7 @@ export function handleShellKeyEvent(store, handlers, event) {
|
|
|
227
228
|
const next = current.jobsPickerIndex + (key.upArrow ? -1 : 1);
|
|
228
229
|
return {
|
|
229
230
|
...current,
|
|
231
|
+
jobsPickerExpandedId: null,
|
|
230
232
|
jobsPickerIndex: Math.max(0, Math.min(next, Math.max(count - 1, 0))),
|
|
231
233
|
};
|
|
232
234
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thegitai/cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.18",
|
|
4
4
|
"description": "TheGitAI CLI client (source-visible, proprietary)",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://thegit.ai",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"@lydell/node-pty-linux-x64": "1.1.0",
|
|
26
26
|
"@lydell/node-pty-win32-arm64": "1.1.0",
|
|
27
27
|
"@lydell/node-pty-win32-x64": "1.1.0",
|
|
28
|
-
"@thegitai/tui-darwin-arm64": "1.0.0-beta.
|
|
29
|
-
"@thegitai/tui-darwin-x64": "1.0.0-beta.
|
|
30
|
-
"@thegitai/tui-linux-x64": "1.0.0-beta.
|
|
31
|
-
"@thegitai/tui-win32-x64": "1.0.0-beta.
|
|
28
|
+
"@thegitai/tui-darwin-arm64": "1.0.0-beta.18",
|
|
29
|
+
"@thegitai/tui-darwin-x64": "1.0.0-beta.18",
|
|
30
|
+
"@thegitai/tui-linux-x64": "1.0.0-beta.18",
|
|
31
|
+
"@thegitai/tui-win32-x64": "1.0.0-beta.18",
|
|
32
32
|
"@vscode/ripgrep": "1.18.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|