Add --pipeline flag and improve log file naming

- Add --pipeline flag for explicit pipeline/non-interactive mode
- Change log file naming from base36 to YYYYMMDD-HHmmss-random format
- Update --task description to clarify it's an alternative to --issue
- Add tests for new timestamp-based session ID format

Resolves #28
This commit is contained in:
nrslib 2026-02-01 08:40:18 +09:00
parent 9ac4f374b8
commit cf393312a2
7 changed files with 27 additions and 12 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "takt", "name": "takt",
"version": "0.3.6", "version": "0.3.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "takt", "name": "takt",
"version": "0.3.6", "version": "0.3.7",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.19", "@anthropic-ai/claude-agent-sdk": "^0.2.19",

View File

@ -1,6 +1,6 @@
{ {
"name": "takt", "name": "takt",
"version": "0.3.6", "version": "0.3.7",
"description": "TAKT: Task Agent Koordination Tool - AI Agent Workflow Orchestration", "description": "TAKT: Task Agent Koordination Tool - AI Agent Workflow Orchestration",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -208,7 +208,7 @@ steps:
permission_mode: acceptEdits permission_mode: acceptEdits
rules: rules:
- condition: AI issues fixed - condition: AI issues fixed
next: reviewers next: ai_review
- condition: Cannot proceed, insufficient info - condition: Cannot proceed, insufficient info
next: plan next: plan
instruction_template: | instruction_template: |

View File

@ -204,7 +204,7 @@ steps:
permission_mode: acceptEdits permission_mode: acceptEdits
rules: rules:
- condition: AI問題の修正完了 - condition: AI問題の修正完了
next: reviewers next: ai_review
- condition: 判断できない、情報不足 - condition: 判断できない、情報不足
next: plan next: plan
instruction_template: | instruction_template: |

View File

@ -52,6 +52,11 @@ describe('generateSessionId', () => {
expect(typeof id).toBe('string'); expect(typeof id).toBe('string');
expect(id.length).toBeGreaterThan(0); expect(id.length).toBeGreaterThan(0);
}); });
it('should follow the new timestamp format', () => {
const id = generateSessionId();
expect(id).toMatch(/^\d{8}-\d{6}-[a-z0-9]{6}$/);
});
}); });
describe('createSessionLog', () => { describe('createSessionLog', () => {

View File

@ -260,7 +260,8 @@ program
.option('--repo <owner/repo>', 'Repository (defaults to current)') .option('--repo <owner/repo>', 'Repository (defaults to current)')
.option('--provider <name>', 'Override agent provider (claude|codex|mock)') .option('--provider <name>', 'Override agent provider (claude|codex|mock)')
.option('--model <name>', 'Override agent model') .option('--model <name>', 'Override agent model')
.option('-t, --task <string>', 'Task content (triggers pipeline/non-interactive mode)') .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('--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');
@ -268,9 +269,9 @@ program
program.hook('preAction', async () => { program.hook('preAction', async () => {
resolvedCwd = resolve(process.cwd()); resolvedCwd = resolve(process.cwd());
// Pipeline mode: triggered by --task (non-interactive) // Pipeline mode: triggered by --pipeline flag
const rootOpts = program.opts(); const rootOpts = program.opts();
pipelineMode = rootOpts.task !== undefined; pipelineMode = rootOpts.pipeline === true;
await initGlobalDirs({ nonInteractive: pipelineMode }); await initGlobalDirs({ nonInteractive: pipelineMode });
initProjectDirs(resolvedCwd); initProjectDirs(resolvedCwd);
@ -390,11 +391,11 @@ program
createWorktree: createWorktreeOverride, createWorktree: createWorktreeOverride,
}; };
// --- Pipeline mode (non-interactive): triggered by --task --- // --- Pipeline mode (non-interactive): triggered by --pipeline ---
if (pipelineMode) { if (pipelineMode) {
const exitCode = await executePipeline({ const exitCode = await executePipeline({
issueNumber: opts.issue as number | undefined, issueNumber: opts.issue as number | undefined,
task: opts.task as string, task: opts.task as string | undefined,
workflow: (opts.workflow as string | undefined) ?? DEFAULT_WORKFLOW_NAME, workflow: (opts.workflow as string | undefined) ?? DEFAULT_WORKFLOW_NAME,
branch: opts.branch as string | undefined, branch: opts.branch as string | undefined,
autoPr: opts.autoPr === true, autoPr: opts.autoPr === true,
@ -413,6 +414,13 @@ program
// --- Normal (interactive) mode --- // --- Normal (interactive) mode ---
// Resolve --task option to task text
const taskFromOption = opts.task as string | undefined;
if (taskFromOption) {
await selectAndExecuteTask(resolvedCwd, taskFromOption, selectOptions, agentOverrides);
return;
}
// Resolve --issue N to task text (same as #N) // Resolve --issue N to task text (same as #N)
const issueFromOption = opts.issue as number | undefined; const issueFromOption = opts.issue as number | undefined;
if (issueFromOption) { if (issueFromOption) {

View File

@ -195,7 +195,10 @@ export function loadNdjsonLog(filepath: string): SessionLog | null {
/** Generate a session ID */ /** Generate a session ID */
export function generateSessionId(): string { export function generateSessionId(): string {
const timestamp = Date.now().toString(36); const now = new Date();
const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}-${String(
now.getHours(),
).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`;
const random = Math.random().toString(36).slice(2, 8); const random = Math.random().toString(36).slice(2, 8);
return `${timestamp}-${random}`; return `${timestamp}-${random}`;
} }
@ -333,4 +336,3 @@ export function updateLatestPointer(
writeFileAtomic(latestPath, JSON.stringify(pointer, null, 2)); writeFileAtomic(latestPath, JSON.stringify(pointer, null, 2));
} }