diff --git a/Taskfile.yml b/Taskfile.yml index 57c4f78..61cf6c7 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -24,11 +24,18 @@ tasks: echo " task stop Stop all containers" echo " task help Show this help message" echo "" + echo "Options for 'start':" + echo " CONFIG= Configuration file (YAML)" + echo " OUTPUT= Output directory for reports" + echo "" + echo "Options for 'stop':" + echo " CLEAN=true Remove all data including volumes" + echo "" echo "Examples:" echo " task start URL=https://example.com REPO=/path/to/repo" - echo " task start URL=https://example.com REPO=/path/to/repo -- --pipeline-testing" - echo " task start URL=https://example.com REPO=/path/to/repo -- --config ./config.yaml" + echo " task start URL=https://example.com REPO=/path/to/repo CONFIG=./config.yaml" echo " task query ID=shannon-1234567890" + echo " task stop CLEAN=true" echo "" echo "Monitor workflows at http://localhost:8233" @@ -52,8 +59,12 @@ tasks: sleep 2 done - | + ARGS="" + {{if .CONFIG}}ARGS="$ARGS --config {{.CONFIG}}"{{end}} + {{if .OUTPUT}}ARGS="$ARGS --output {{.OUTPUT}}"{{end}} + {{if eq .PIPELINE_TESTING "true"}}ARGS="$ARGS --pipeline-testing"{{end}} docker compose -f {{.COMPOSE_FILE}} exec -T worker \ - node dist/temporal/client.js "{{.URL}}" "/target-repo" {{.CLI_ARGS}} + node dist/temporal/client.js "{{.URL}}" "/target-repo" $ARGS {{.CLI_ARGS}} logs: desc: View real-time worker logs @@ -76,8 +87,8 @@ tasks: silent: true cmds: - | - if [ "{{.CLI_ARGS}}" = "--clean" ]; then - docker compose -f {{.COMPOSE_FILE}} down -v - else - docker compose -f {{.COMPOSE_FILE}} down - fi + {{if eq .CLEAN "true"}} + docker compose -f {{.COMPOSE_FILE}} down -v + {{else}} + docker compose -f {{.COMPOSE_FILE}} down + {{end}} diff --git a/src/ai/claude-executor.ts b/src/ai/claude-executor.ts index 91ef833..8675692 100644 --- a/src/ai/claude-executor.ts +++ b/src/ai/claude-executor.ts @@ -15,7 +15,6 @@ import { timingResults, Timer } from '../utils/metrics.js'; import { formatTimestamp } from '../utils/formatting.js'; import { createGitCheckpoint, commitGitSuccess, rollbackGitWorkspace, getGitCommitHash } from '../utils/git-manager.js'; import { AGENT_VALIDATORS, MCP_AGENT_MAPPING } from '../constants.js'; -import { generateSessionLogPath } from '../session-manager.js'; import { AuditSession } from '../audit/index.js'; import { createShannonHelperServer } from '../../mcp-server/dist/index.js'; import type { SessionMetadata } from '../audit/utils.js'; @@ -39,7 +38,6 @@ export interface ClaudePromptResult { cost: number; partialCost?: number; apiErrorDetected?: boolean; - logFile?: string; error?: string; errorType?: string; prompt?: string; @@ -215,10 +213,7 @@ export async function runClaudePrompt( ); const auditLogger = createAuditLogger(auditSession); - const logFilePath = buildLogFilePath(sessionMetadata, execContext.agentKey, attemptNumber); - if (!logFilePath) { - console.log(chalk.blue(` Running Claude Code: ${description}...`)); - } + console.log(chalk.blue(` Running Claude Code: ${description}...`)); const mcpServers = buildMcpServers(sourceDir, agentName); const options = { @@ -262,7 +257,7 @@ export async function runClaudePrompt( progress.finish(formatCompletionMessage(execContext, description, turnCount, duration)); - const returnData: ClaudePromptResult = { + return { result, success: true, duration, @@ -271,10 +266,6 @@ export async function runClaudePrompt( partialCost: totalCost, apiErrorDetected }; - if (logFilePath) { - returnData.logFile = logFilePath; - } - return returnData; } catch (error) { const duration = timer.stop(); @@ -299,18 +290,6 @@ export async function runClaudePrompt( } } -function buildLogFilePath( - sessionMetadata: SessionMetadata | null, - agentKey: string, - attemptNumber: number -): string | null { - if (!sessionMetadata || !sessionMetadata.webUrl || !sessionMetadata.id) { - return null; - } - const timestamp = formatTimestamp().replace(/T/, '_').replace(/[:.]/g, '-').slice(0, 19); - const logDir = generateSessionLogPath(sessionMetadata.webUrl, sessionMetadata.id); - return path.join(logDir, `${timestamp}_${agentKey}_attempt-${attemptNumber}.log`); -} interface MessageLoopResult { turnCount: number; diff --git a/src/audit/utils.ts b/src/audit/utils.ts index b05d0d6..18d7bd2 100644 --- a/src/audit/utils.ts +++ b/src/audit/utils.ts @@ -31,12 +31,18 @@ export interface SessionMetadata { } /** - * Generate standardized session identifier: {hostname}_{sessionId} + * Extract and sanitize hostname from URL for use in identifiers + */ +export function sanitizeHostname(url: string): string { + return new URL(url).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); +} + +/** + * Generate standardized session identifier from workflow ID + * Workflow IDs already contain hostname, so we use them directly */ export function generateSessionIdentifier(sessionMetadata: SessionMetadata): string { - const { id, webUrl } = sessionMetadata; - const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); - return `${hostname}_${id}`; + return sessionMetadata.id; } /** diff --git a/src/session-manager.ts b/src/session-manager.ts index 7a31d1a..335a74d 100644 --- a/src/session-manager.ts +++ b/src/session-manager.ts @@ -126,10 +126,4 @@ export const AGENT_PHASE_MAP: Readonly> = Object.fr 'report': 'reporting', }); -// Generate a session-based log folder path (used by claude-executor.ts) -export const generateSessionLogPath = (webUrl: string, sessionId: string): string => { - const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); - const sessionFolderName = `${hostname}_${sessionId}`; - return path.join(process.cwd(), 'agent-logs', sessionFolderName); -}; diff --git a/src/temporal/client.ts b/src/temporal/client.ts index 8278e19..e16e711 100644 --- a/src/temporal/client.ts +++ b/src/temporal/client.ts @@ -30,6 +30,7 @@ import { Connection, Client } from '@temporalio/client'; import dotenv from 'dotenv'; import chalk from 'chalk'; import { displaySplashScreen } from '../splash-screen.js'; +import { sanitizeHostname } from '../audit/utils.js'; // Import types only - these don't pull in workflow runtime code import type { PipelineInput, PipelineState, PipelineProgress } from './shared.js'; @@ -126,7 +127,8 @@ async function startPipeline(): Promise { const client = new Client({ connection }); try { - const workflowId = customWorkflowId || `shannon-${Date.now()}`; + const hostname = sanitizeHostname(webUrl); + const workflowId = customWorkflowId || `${hostname}_shannon-${Date.now()}`; const input: PipelineInput = { webUrl,