feat(setup): delete scratch agent after ping-pong, simplify flow
The "Terminal Agent" created for the connection test is now silently
deleted after a successful ping. If the user chooses to chat, a new
agent is auto-created as "{name}'s Terminal" — no name prompt needed.
Condensed the three-line ping section into a single "Connection verified."
status line.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
exe.dev user
parent
9e45845000
commit
d86051805b
74
scripts/delete-cli-agent.ts
Normal file
74
scripts/delete-cli-agent.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Delete the scratch CLI agent created during setup's ping-pong test.
|
||||||
|
*
|
||||||
|
* Removes the agent group, its messaging_group_agents wiring, any
|
||||||
|
* agent_destinations rows, and the groups/<folder>/ directory. Leaves the
|
||||||
|
* CLI messaging group intact so it can be reused for a new agent.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* pnpm exec tsx scripts/delete-cli-agent.ts --folder <folder-name>
|
||||||
|
*/
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { DATA_DIR } from '../src/config.js';
|
||||||
|
import { getAgentGroupByFolder, deleteAgentGroup } from '../src/db/agent-groups.js';
|
||||||
|
import { initDb } from '../src/db/connection.js';
|
||||||
|
import { runMigrations } from '../src/db/migrations/index.js';
|
||||||
|
|
||||||
|
interface Args {
|
||||||
|
folder: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(): Args {
|
||||||
|
const argv = process.argv.slice(2);
|
||||||
|
let folder = '';
|
||||||
|
for (let i = 0; i < argv.length; i++) {
|
||||||
|
if (argv[i] === '--folder' && argv[i + 1]) folder = argv[++i];
|
||||||
|
}
|
||||||
|
if (!folder) {
|
||||||
|
console.error('usage: pnpm exec tsx scripts/delete-cli-agent.ts --folder <folder-name>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return { folder };
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = parseArgs();
|
||||||
|
|
||||||
|
const db = initDb(path.join(DATA_DIR, 'v2.db'));
|
||||||
|
runMigrations(db);
|
||||||
|
|
||||||
|
const ag = getAgentGroupByFolder(args.folder);
|
||||||
|
if (!ag) {
|
||||||
|
console.log(`No agent group with folder "${args.folder}" — nothing to delete.`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all rows referencing this agent group, in dependency order.
|
||||||
|
const fkTables = [
|
||||||
|
'messaging_group_agents',
|
||||||
|
'agent_destinations',
|
||||||
|
'agent_group_members',
|
||||||
|
'pending_sender_approvals',
|
||||||
|
'channel_registrations',
|
||||||
|
'user_roles',
|
||||||
|
'sessions',
|
||||||
|
];
|
||||||
|
for (const table of fkTables) {
|
||||||
|
const exists = db
|
||||||
|
.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?")
|
||||||
|
.get(table);
|
||||||
|
if (exists) {
|
||||||
|
db.prepare(`DELETE FROM ${table} WHERE agent_group_id = ?`).run(ag.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAgentGroup(ag.id);
|
||||||
|
|
||||||
|
// Remove the groups/<folder>/ directory.
|
||||||
|
const groupDir = path.join(process.cwd(), 'groups', args.folder);
|
||||||
|
if (fs.existsSync(groupDir)) {
|
||||||
|
fs.rmSync(groupDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Deleted agent group ${ag.id} (${args.folder}).`);
|
||||||
@@ -55,6 +55,7 @@ import { ensureAnswer, fail, runQuietChild, runQuietStep } from './lib/runner.js
|
|||||||
import { emit as phEmit } from './lib/diagnostics.js';
|
import { emit as phEmit } from './lib/diagnostics.js';
|
||||||
import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, note, wrapForGutter } from './lib/theme.js';
|
import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, note, wrapForGutter } from './lib/theme.js';
|
||||||
import { isValidTimezone } from '../src/timezone.js';
|
import { isValidTimezone } from '../src/timezone.js';
|
||||||
|
import { normalizeName } from '../src/modules/agent-to-agent/db/agent-destinations.js';
|
||||||
|
|
||||||
const CLI_AGENT_NAME = 'Terminal Agent';
|
const CLI_AGENT_NAME = 'Terminal Agent';
|
||||||
const RUN_START = Date.now();
|
const RUN_START = Date.now();
|
||||||
@@ -349,8 +350,8 @@ async function main(): Promise<void> {
|
|||||||
const res = await runQuietStep(
|
const res = await runQuietStep(
|
||||||
'cli-agent',
|
'cli-agent',
|
||||||
{
|
{
|
||||||
running: 'Bringing your assistant online…',
|
running: 'Preparing connection test…',
|
||||||
done: 'Assistant wired up.',
|
done: 'Ready to test.',
|
||||||
},
|
},
|
||||||
['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME],
|
['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME],
|
||||||
);
|
);
|
||||||
@@ -365,7 +366,7 @@ async function main(): Promise<void> {
|
|||||||
p.log.message(
|
p.log.message(
|
||||||
brandBody(
|
brandBody(
|
||||||
dimWrap(
|
dimWrap(
|
||||||
"Your assistant runs in an isolated sandbox. I'm going to send it a quick test message (ping) and wait for a reply (pong) to confirm it's responding. First startup typically takes 30–60 seconds while the sandbox warms up.",
|
'Checking your assistant can respond — first startup takes 30–60 seconds.',
|
||||||
4,
|
4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -373,6 +374,10 @@ async function main(): Promise<void> {
|
|||||||
const ping = await confirmAssistantResponds();
|
const ping = await confirmAssistantResponds();
|
||||||
if (ping === 'ok') {
|
if (ping === 'ok') {
|
||||||
phEmit('first_chat_ready');
|
phEmit('first_chat_ready');
|
||||||
|
const scratchFolder = `cli-with-${normalizeName(displayName!)}`;
|
||||||
|
spawnSync('pnpm', ['exec', 'tsx', 'scripts/delete-cli-agent.ts', '--folder', scratchFolder], {
|
||||||
|
stdio: 'ignore',
|
||||||
|
});
|
||||||
const next = ensureAnswer(
|
const next = ensureAnswer(
|
||||||
await brightSelect<'continue' | 'chat'>({
|
await brightSelect<'continue' | 'chat'>({
|
||||||
message: 'What next?',
|
message: 'What next?',
|
||||||
@@ -390,7 +395,23 @@ async function main(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
) as 'continue' | 'chat';
|
) as 'continue' | 'chat';
|
||||||
setupLog.userInput('first_chat_choice', next);
|
setupLog.userInput('first_chat_choice', next);
|
||||||
if (next === 'chat') await runFirstChat();
|
if (next === 'chat') {
|
||||||
|
const terminalAgentName = `${displayName!}'s Terminal`;
|
||||||
|
const createRes = await runQuietChild(
|
||||||
|
'create-terminal-agent',
|
||||||
|
'pnpm',
|
||||||
|
['exec', 'tsx', 'scripts/init-cli-agent.ts', '--display-name', displayName!, '--agent-name', terminalAgentName],
|
||||||
|
{ running: `Creating ${terminalAgentName}…`, done: `${terminalAgentName} is ready.` },
|
||||||
|
);
|
||||||
|
if (!createRes.ok) {
|
||||||
|
await fail(
|
||||||
|
'create-terminal-agent',
|
||||||
|
`Couldn't create ${terminalAgentName}.`,
|
||||||
|
'You can retry later with `pnpm exec tsx scripts/init-cli-agent.ts`.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await runFirstChat();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
phEmit('first_chat_failed', { reason: ping });
|
phEmit('first_chat_failed', { reason: ping });
|
||||||
renderPingFailureNote(ping);
|
renderPingFailureNote(ping);
|
||||||
@@ -592,7 +613,7 @@ async function confirmAssistantResponds(): Promise<PingResult> {
|
|||||||
const elapsed = Math.round((Date.now() - start) / 1000);
|
const elapsed = Math.round((Date.now() - start) / 1000);
|
||||||
const suffix = ` (${elapsed}s)`;
|
const suffix = ` (${elapsed}s)`;
|
||||||
if (result === 'ok') {
|
if (result === 'ok') {
|
||||||
s.stop(`${k.bold(fitToWidth('Your assistant is ready.', suffix))}${k.dim(suffix)}`);
|
s.stop(`${k.bold(fitToWidth('Connection verified.', suffix))}${k.dim(suffix)}`);
|
||||||
} else {
|
} else {
|
||||||
const msg =
|
const msg =
|
||||||
result === 'socket_error' ? "Couldn't reach the NanoClaw service." : "Your assistant didn't reply in time.";
|
result === 'socket_error' ? "Couldn't reach the NanoClaw service." : "Your assistant didn't reply in time.";
|
||||||
|
|||||||
Reference in New Issue
Block a user