Merge pull request #80 from nrslib/takt/issue-70-1769924108
CI用にログ出力最小モードを導入する
This commit is contained in:
commit
29841f0c51
20
src/cli.ts
20
src/cli.ts
@ -66,6 +66,9 @@ let resolvedCwd = '';
|
||||
/** Whether pipeline mode is active (--task specified, set in preAction) */
|
||||
let pipelineMode = false;
|
||||
|
||||
/** Whether quiet mode is active (--quiet flag or config, set in preAction) */
|
||||
let quietMode = false;
|
||||
|
||||
export interface WorktreeConfirmationResult {
|
||||
execCwd: string;
|
||||
isWorktree: boolean;
|
||||
@ -263,7 +266,8 @@ program
|
||||
.option('-t, --task <string>', 'Task content (as alternative to GitHub issue)')
|
||||
.option('--pipeline', 'Pipeline mode: non-interactive, no worktree, direct branch creation')
|
||||
.option('--skip-git', 'Skip branch creation, commit, and push (pipeline mode)')
|
||||
.option('--create-worktree <yes|no>', 'Skip the worktree prompt by explicitly specifying yes or no');
|
||||
.option('--create-worktree <yes|no>', 'Skip the worktree prompt by explicitly specifying yes or no')
|
||||
.option('-q, --quiet', 'Minimal output mode: suppress AI output (for CI)');
|
||||
|
||||
// Common initialization for all commands
|
||||
program.hook('preAction', async () => {
|
||||
@ -285,17 +289,27 @@ program.hook('preAction', async () => {
|
||||
|
||||
initDebugLogger(debugConfig, resolvedCwd);
|
||||
|
||||
// Load config once for both log level and quiet mode
|
||||
const config = loadGlobalConfig();
|
||||
|
||||
if (verbose) {
|
||||
setVerboseConsole(true);
|
||||
setLogLevel('debug');
|
||||
} else {
|
||||
const config = loadGlobalConfig();
|
||||
setLogLevel(config.logLevel);
|
||||
}
|
||||
|
||||
log.info('TAKT CLI starting', { version: cliVersion, cwd: resolvedCwd, verbose, pipelineMode });
|
||||
// Quiet mode: CLI flag takes precedence over config
|
||||
quietMode = rootOpts.quiet === true || config.minimalOutput === true;
|
||||
|
||||
log.info('TAKT CLI starting', { version: cliVersion, cwd: resolvedCwd, verbose, pipelineMode, quietMode });
|
||||
});
|
||||
|
||||
/** Get whether quiet mode is active (CLI flag or config, resolved in preAction) */
|
||||
export function isQuietMode(): boolean {
|
||||
return quietMode;
|
||||
}
|
||||
|
||||
// --- Subcommands ---
|
||||
|
||||
program
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
import * as readline from 'node:readline';
|
||||
import chalk from 'chalk';
|
||||
import { loadGlobalConfig } from '../config/globalConfig.js';
|
||||
import { isQuietMode } from '../cli.js';
|
||||
import { loadAgentSessions, updateAgentSession } from '../config/paths.js';
|
||||
import { getProvider, type ProviderType } from '../providers/index.js';
|
||||
import { createLogger } from '../utils/debug.js';
|
||||
@ -151,14 +152,14 @@ export async function interactiveMode(cwd: string, initialInput?: string): Promi
|
||||
|
||||
/** Call AI with automatic retry on session error (stale/invalid session ID). */
|
||||
async function callAIWithRetry(prompt: string): Promise<CallAIResult | null> {
|
||||
const display = new StreamDisplay('assistant');
|
||||
const display = new StreamDisplay('assistant', isQuietMode());
|
||||
try {
|
||||
const result = await callAI(provider, prompt, cwd, model, sessionId, display);
|
||||
// If session failed, clear it and retry without session
|
||||
if (!result.success && sessionId) {
|
||||
log.info('Session invalid, retrying without session');
|
||||
sessionId = undefined;
|
||||
const retryDisplay = new StreamDisplay('assistant');
|
||||
const retryDisplay = new StreamDisplay('assistant', isQuietMode());
|
||||
const retry = await callAI(provider, prompt, cwd, model, undefined, retryDisplay);
|
||||
if (retry.sessionId) {
|
||||
sessionId = retry.sessionId;
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
updateWorktreeSession,
|
||||
} from '../config/paths.js';
|
||||
import { loadGlobalConfig } from '../config/globalConfig.js';
|
||||
import { isQuietMode } from '../cli.js';
|
||||
import {
|
||||
header,
|
||||
info,
|
||||
@ -200,7 +201,8 @@ export async function executeWorkflow(
|
||||
log.debug('Step instruction', instruction);
|
||||
}
|
||||
|
||||
displayRef.current = new StreamDisplay(step.agentDisplayName);
|
||||
// Use quiet mode from CLI (already resolved CLI flag + config in preAction)
|
||||
displayRef.current = new StreamDisplay(step.agentDisplayName, isQuietMode());
|
||||
|
||||
// Write step_start record to NDJSON log
|
||||
const record: NdjsonStepStart = {
|
||||
|
||||
@ -52,6 +52,7 @@ export function loadGlobalConfig(): GlobalConfig {
|
||||
commitMessageTemplate: parsed.pipeline.commit_message_template,
|
||||
prBodyTemplate: parsed.pipeline.pr_body_template,
|
||||
} : undefined,
|
||||
minimalOutput: parsed.minimal_output,
|
||||
};
|
||||
}
|
||||
|
||||
@ -95,6 +96,9 @@ export function saveGlobalConfig(config: GlobalConfig): void {
|
||||
raw.pipeline = pipelineRaw;
|
||||
}
|
||||
}
|
||||
if (config.minimalOutput !== undefined) {
|
||||
raw.minimal_output = config.minimalOutput;
|
||||
}
|
||||
writeFileSync(configPath, stringifyYaml(raw), 'utf-8');
|
||||
}
|
||||
|
||||
|
||||
@ -185,6 +185,8 @@ export const GlobalConfigSchema = z.object({
|
||||
openai_api_key: z.string().optional(),
|
||||
/** Pipeline execution settings */
|
||||
pipeline: PipelineConfigSchema.optional(),
|
||||
/** Minimal output mode for CI - suppress AI output to prevent sensitive information leaks */
|
||||
minimal_output: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
/** Project config schema */
|
||||
|
||||
@ -207,6 +207,8 @@ export interface GlobalConfig {
|
||||
openaiApiKey?: string;
|
||||
/** Pipeline execution settings */
|
||||
pipeline?: PipelineConfig;
|
||||
/** Minimal output mode for CI - suppress AI output to prevent sensitive information leaks */
|
||||
minimalOutput?: boolean;
|
||||
}
|
||||
|
||||
/** Project-level configuration */
|
||||
|
||||
@ -166,10 +166,14 @@ export class StreamDisplay {
|
||||
private spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
||||
private spinnerFrame = 0;
|
||||
|
||||
constructor(private agentName = 'Claude') {}
|
||||
constructor(
|
||||
private agentName = 'Claude',
|
||||
private quiet = false,
|
||||
) {}
|
||||
|
||||
/** Display initialization event */
|
||||
showInit(model: string): void {
|
||||
if (this.quiet) return;
|
||||
console.log(chalk.gray(`[${this.agentName}] Model: ${model}`));
|
||||
}
|
||||
|
||||
@ -202,6 +206,8 @@ export class StreamDisplay {
|
||||
|
||||
/** Display tool use event */
|
||||
showToolUse(tool: string, input: Record<string, unknown>): void {
|
||||
if (this.quiet) return;
|
||||
|
||||
// Clear any buffered text first
|
||||
this.flushText();
|
||||
|
||||
@ -216,6 +222,7 @@ export class StreamDisplay {
|
||||
|
||||
/** Display tool output streaming */
|
||||
showToolOutput(output: string, tool?: string): void {
|
||||
if (this.quiet) return;
|
||||
if (!output) return;
|
||||
this.stopToolSpinner();
|
||||
this.flushThinking();
|
||||
@ -238,9 +245,22 @@ export class StreamDisplay {
|
||||
|
||||
/** Display tool result event */
|
||||
showToolResult(content: string, isError: boolean): void {
|
||||
// Stop the spinner first
|
||||
// Stop the spinner first (always, even in quiet mode to prevent spinner artifacts)
|
||||
this.stopToolSpinner();
|
||||
|
||||
if (this.quiet) {
|
||||
// In quiet mode: show errors but suppress success messages
|
||||
if (isError) {
|
||||
const toolName = this.lastToolUse || 'Tool';
|
||||
const errorContent = content || 'Unknown error';
|
||||
console.log(chalk.red(` ✗ ${toolName}:`), chalk.red(truncate(errorContent, 70)));
|
||||
}
|
||||
this.lastToolUse = null;
|
||||
this.currentToolInputPreview = null;
|
||||
this.toolOutputPrinted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.toolOutputBuffer) {
|
||||
this.printToolOutputLines([this.toolOutputBuffer], this.lastToolUse ?? undefined);
|
||||
this.toolOutputBuffer = '';
|
||||
@ -264,6 +284,8 @@ export class StreamDisplay {
|
||||
|
||||
/** Display streaming thinking (Claude's internal reasoning) */
|
||||
showThinking(thinking: string): void {
|
||||
if (this.quiet) return;
|
||||
|
||||
// Stop spinner if running
|
||||
this.stopToolSpinner();
|
||||
// Flush any regular text first
|
||||
@ -292,6 +314,8 @@ export class StreamDisplay {
|
||||
|
||||
/** Display streaming text (accumulated) */
|
||||
showText(text: string): void {
|
||||
if (this.quiet) return;
|
||||
|
||||
// Stop spinner if running
|
||||
this.stopToolSpinner();
|
||||
// Flush any thinking first
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user