feat: Add agent preview integration with auto-teardown and simplified commands
- Add agentId label to preview containers (cw.agent-id) for tracking - Add startForAgent/stopByAgentId methods to PreviewManager - Auto-teardown: previews torn down on agent:stopped event - Conditional preview prompt injection for execute/refine/discuss agents - Agent-simplified CLI: cw preview start/stop --agent <id> - cw preview setup command with --auto mode for guided config generation - hasPreviewConfig hint on cw project register output - New tRPC procedures: startPreviewForAgent, stopPreviewByAgent
This commit is contained in:
@@ -1038,6 +1038,11 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
|
||||
console.log(`Registered project: ${project.id}`);
|
||||
console.log(` Name: ${project.name}`);
|
||||
console.log(` URL: ${project.url}`);
|
||||
if (project.hasPreviewConfig) {
|
||||
console.log(' Preview: .cw-preview.yml detected — preview deployments ready');
|
||||
} else {
|
||||
console.log(' Preview: No .cw-preview.yml found. Run `cw preview setup` for instructions.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to register project:', (error as Error).message);
|
||||
process.exit(1);
|
||||
@@ -1335,23 +1340,36 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
|
||||
.description('Manage Docker-based preview deployments');
|
||||
|
||||
// cw preview start --initiative <id> --project <id> --branch <branch> [--phase <id>]
|
||||
// cw preview start --agent <id> (agent-simplified: server resolves everything)
|
||||
previewCommand
|
||||
.command('start')
|
||||
.description('Start a preview deployment')
|
||||
.requiredOption('--initiative <id>', 'Initiative ID')
|
||||
.requiredOption('--project <id>', 'Project ID')
|
||||
.requiredOption('--branch <branch>', 'Branch to deploy')
|
||||
.option('--initiative <id>', 'Initiative ID')
|
||||
.option('--project <id>', 'Project ID')
|
||||
.option('--branch <branch>', 'Branch to deploy')
|
||||
.option('--phase <id>', 'Phase ID')
|
||||
.action(async (options: { initiative: string; project: string; branch: string; phase?: string }) => {
|
||||
.option('--agent <id>', 'Agent ID (server resolves initiative/project/branch)')
|
||||
.action(async (options: { initiative?: string; project?: string; branch?: string; phase?: string; agent?: string }) => {
|
||||
try {
|
||||
const client = createDefaultTrpcClient();
|
||||
console.log('Starting preview deployment...');
|
||||
const preview = await client.startPreview.mutate({
|
||||
initiativeId: options.initiative,
|
||||
projectId: options.project,
|
||||
branch: options.branch,
|
||||
phaseId: options.phase,
|
||||
});
|
||||
|
||||
let preview;
|
||||
if (options.agent) {
|
||||
preview = await client.startPreviewForAgent.mutate({ agentId: options.agent });
|
||||
} else {
|
||||
if (!options.initiative || !options.project || !options.branch) {
|
||||
console.error('Either --agent or all of --initiative, --project, --branch are required');
|
||||
process.exit(1);
|
||||
}
|
||||
preview = await client.startPreview.mutate({
|
||||
initiativeId: options.initiative,
|
||||
projectId: options.project,
|
||||
branch: options.branch,
|
||||
phaseId: options.phase,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Preview started: ${preview.id}`);
|
||||
console.log(` URL: ${preview.url}`);
|
||||
console.log(` Branch: ${preview.branch}`);
|
||||
@@ -1365,14 +1383,24 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
|
||||
});
|
||||
|
||||
// cw preview stop <previewId>
|
||||
// cw preview stop --agent <id>
|
||||
previewCommand
|
||||
.command('stop <previewId>')
|
||||
.command('stop [previewId]')
|
||||
.description('Stop a preview deployment')
|
||||
.action(async (previewId: string) => {
|
||||
.option('--agent <id>', 'Stop preview by agent ID')
|
||||
.action(async (previewId: string | undefined, options: { agent?: string }) => {
|
||||
try {
|
||||
const client = createDefaultTrpcClient();
|
||||
await client.stopPreview.mutate({ previewId });
|
||||
console.log(`Preview '${previewId}' stopped`);
|
||||
if (options.agent) {
|
||||
await client.stopPreviewByAgent.mutate({ agentId: options.agent });
|
||||
console.log(`Previews for agent '${options.agent}' stopped`);
|
||||
} else if (previewId) {
|
||||
await client.stopPreview.mutate({ previewId });
|
||||
console.log(`Preview '${previewId}' stopped`);
|
||||
} else {
|
||||
console.error('Either <previewId> or --agent <id> is required');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to stop preview:', (error as Error).message);
|
||||
process.exit(1);
|
||||
@@ -1434,6 +1462,138 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
|
||||
}
|
||||
});
|
||||
|
||||
// cw preview setup [--auto --project <id>]
|
||||
previewCommand
|
||||
.command('setup')
|
||||
.description('Show preview deployment setup instructions')
|
||||
.option('--auto', 'Auto-create initiative and spawn agent to set up preview config')
|
||||
.option('--project <id>', 'Project ID (required with --auto)')
|
||||
.option('--provider <name>', 'Agent provider (optional, with --auto)')
|
||||
.action(async (options: { auto?: boolean; project?: string; provider?: string }) => {
|
||||
if (!options.auto) {
|
||||
console.log(`Preview Deployment Setup
|
||||
========================
|
||||
|
||||
Prerequisites:
|
||||
- Docker installed and running
|
||||
- Project registered in Codewalkers (cw project register)
|
||||
|
||||
Step 1: Add .cw-preview.yml to your project root
|
||||
|
||||
Minimal (single service with Dockerfile):
|
||||
|
||||
version: 1
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
port: 3000
|
||||
|
||||
Multi-service (frontend + API + database):
|
||||
|
||||
version: 1
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/web/Dockerfile
|
||||
port: 3000
|
||||
route: /
|
||||
healthcheck:
|
||||
path: /
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: packages/api/Dockerfile
|
||||
port: 8080
|
||||
route: /api
|
||||
healthcheck:
|
||||
path: /health
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
internal: true
|
||||
env:
|
||||
POSTGRES_PASSWORD: preview
|
||||
|
||||
Step 2: Commit .cw-preview.yml to your repo
|
||||
|
||||
Step 3: Start a preview
|
||||
cw preview start --initiative <id> --project <id> --branch <branch>
|
||||
|
||||
Without .cw-preview.yml, previews auto-discover:
|
||||
1. docker-compose.yml / compose.yml
|
||||
2. Dockerfile (single service, port 3000)
|
||||
|
||||
Full reference: docs/preview.md`);
|
||||
return;
|
||||
}
|
||||
|
||||
// --auto mode
|
||||
if (!options.project) {
|
||||
console.error('--project <id> is required with --auto');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const client = createDefaultTrpcClient();
|
||||
|
||||
// Look up project
|
||||
const projects = await client.listProjects.query();
|
||||
const project = projects.find((p: { id: string }) => p.id === options.project);
|
||||
if (!project) {
|
||||
console.error(`Project '${options.project}' not found`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Create initiative
|
||||
const initiative = await client.createInitiative.mutate({
|
||||
name: `Preview setup: ${project.name}`,
|
||||
projectIds: [project.id],
|
||||
});
|
||||
|
||||
// Create root page with setup guide
|
||||
const rootPage = await client.getRootPage.query({ initiativeId: initiative.id });
|
||||
|
||||
const { markdownToTiptapJson } = await import('../agent/markdown-to-tiptap.js');
|
||||
const setupMarkdown = `# Preview Setup for ${project.name}
|
||||
|
||||
Analyze the project structure and create a \`.cw-preview.yml\` configuration file for Docker-based preview deployments.
|
||||
|
||||
## What to do
|
||||
|
||||
1. Examine the project's build system, frameworks, and service architecture
|
||||
2. Identify all services (frontend, backend, database, etc.)
|
||||
3. Create a \`.cw-preview.yml\` at the project root with appropriate service definitions
|
||||
4. Include dev mode configuration for hot-reload support
|
||||
5. Add healthcheck endpoints where applicable
|
||||
6. Commit the file to the repository
|
||||
|
||||
## Reference
|
||||
|
||||
See the Codewalkers documentation for .cw-preview.yml format and options.`;
|
||||
|
||||
await client.updatePage.mutate({
|
||||
id: rootPage.id,
|
||||
content: JSON.stringify(markdownToTiptapJson(setupMarkdown)),
|
||||
});
|
||||
|
||||
// Spawn refine agent
|
||||
const spawnInput: { initiativeId: string; instruction: string; provider?: string } = {
|
||||
initiativeId: initiative.id,
|
||||
instruction: 'Analyze the project and create a .cw-preview.yml for preview deployments. Follow the setup guide in the initiative pages.',
|
||||
};
|
||||
if (options.provider) {
|
||||
spawnInput.provider = options.provider;
|
||||
}
|
||||
const agent = await client.spawnArchitectRefine.mutate(spawnInput);
|
||||
|
||||
console.log(`Created initiative: ${initiative.id}`);
|
||||
console.log(`Spawned agent: ${agent.id} (${agent.name})`);
|
||||
} catch (error) {
|
||||
console.error('Failed to set up preview:', (error as Error).message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// Inter-agent conversation commands
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user