From f6cc3736b207ca7af756c5bff4edccb88111d282 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Tue, 30 Dec 2025 14:51:25 +0800 Subject: [PATCH] feat(cli): support stdin input for issue creation and solution commands --- .claude/commands/issue/new.md | 24 +++++++++++++++++++----- ccw/src/commands/issue.ts | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/.claude/commands/issue/new.md b/.claude/commands/issue/new.md index 4c27d36f..51c02059 100644 --- a/.claude/commands/issue/new.md +++ b/.claude/commands/issue/new.md @@ -175,7 +175,16 @@ if (clarityScore < 2 && (!issueData.context || issueData.context.length < 20)) { **Issue Creation** (via CLI endpoint): ```bash -ccw issue create --data '{"title":"...", "context":"...", "priority":3, ...}' +# Option 1: Pipe input (recommended for complex JSON - avoids shell escaping) +echo '{"title":"...", "context":"...", "priority":3}' | ccw issue create + +# Option 2: Heredoc (for multi-line JSON) +ccw issue create << 'EOF' +{"title":"...", "context":"含\"引号\"的内容", "priority":3} +EOF + +# Option 3: --data parameter (simple cases only) +ccw issue create --data '{"title":"...", "priority":3}' ``` **CLI Endpoint Features:** @@ -187,15 +196,20 @@ ccw issue create --data '{"title":"...", "context":"...", "priority":3, ...}' **Example:** ```bash -# Create issue via CLI -ccw issue create --data '{ +# Create issue via pipe (recommended) +echo '{"title": "Login fails with special chars", "context": "500 error when password contains quotes", "priority": 2}' | ccw issue create + +# Or with heredoc for complex JSON +ccw issue create << 'EOF' +{ "title": "Login fails with special chars", - "context": "500 error when password contains quotes", + "context": "500 error when password contains \"quotes\"", "priority": 2, "source": "text", "expected_behavior": "Login succeeds", "actual_behavior": "500 Internal Server Error" -}' +} +EOF # Output (JSON) { diff --git a/ccw/src/commands/issue.ts b/ccw/src/commands/issue.ts index 268ee691..f2481112 100644 --- a/ccw/src/commands/issue.ts +++ b/ccw/src/commands/issue.ts @@ -617,17 +617,30 @@ function generateQueueItemId(queue: Queue, level: 'solution' | 'task' = 'solutio /** * create - Create issue from JSON data * Usage: ccw issue create --data '{"title":"...", "context":"..."}' + * echo '{"title":"..."}' | ccw issue create * Output: JSON with created issue (includes auto-generated ID) */ async function createAction(options: IssueOptions): Promise { - if (!options.data) { + let jsonData: string | undefined = options.data; + + // Support stdin pipe input (avoids shell escaping issues) + if (!jsonData && !process.stdin.isTTY) { + try { + jsonData = readFileSync(0, 'utf-8').trim(); + } catch { + // stdin not available or empty + } + } + + if (!jsonData) { console.error(chalk.red('JSON data required')); console.error(chalk.gray('Usage: ccw issue create --data \'{"title":"...", "context":"..."}\'')); + console.error(chalk.gray(' echo \'{"title":"..."}\' | ccw issue create')); process.exit(1); } try { - const data = JSON.parse(options.data); + const data = JSON.parse(jsonData); const issue = createIssue(data); console.log(JSON.stringify(issue, null, 2)); } catch (err) { @@ -639,23 +652,37 @@ async function createAction(options: IssueOptions): Promise { /** * solution - Create solution from JSON data * Usage: ccw issue solution --data '{"tasks":[...]}' + * echo '{"tasks":[...]}' | ccw issue solution * Output: JSON with created solution (includes auto-generated ID) */ async function solutionAction(issueId: string | undefined, options: IssueOptions): Promise { if (!issueId) { console.error(chalk.red('Issue ID required')); console.error(chalk.gray('Usage: ccw issue solution --data \'{"tasks":[...]}\'')); + console.error(chalk.gray(' echo \'{"tasks":[...]}\' | ccw issue solution ')); process.exit(1); } - if (!options.data) { + let jsonData: string | undefined = options.data; + + // Support stdin pipe input (avoids shell escaping issues) + if (!jsonData && !process.stdin.isTTY) { + try { + jsonData = readFileSync(0, 'utf-8').trim(); + } catch { + // stdin not available or empty + } + } + + if (!jsonData) { console.error(chalk.red('JSON data required')); console.error(chalk.gray('Usage: ccw issue solution --data \'{"tasks":[...]}\'')); + console.error(chalk.gray(' echo \'{"tasks":[...]}\' | ccw issue solution ')); process.exit(1); } try { - const data = JSON.parse(options.data); + const data = JSON.parse(jsonData); const solution = createSolution(issueId, data); console.log(JSON.stringify(solution, null, 2)); } catch (err) {