feat(cli): support stdin input for issue creation and solution commands

This commit is contained in:
catlog22
2025-12-30 14:51:25 +08:00
parent 6e99cd97ca
commit f6cc3736b2
2 changed files with 50 additions and 9 deletions

View File

@@ -175,7 +175,16 @@ if (clarityScore < 2 && (!issueData.context || issueData.context.length < 20)) {
**Issue Creation** (via CLI endpoint): **Issue Creation** (via CLI endpoint):
```bash ```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:** **CLI Endpoint Features:**
@@ -187,15 +196,20 @@ ccw issue create --data '{"title":"...", "context":"...", "priority":3, ...}'
**Example:** **Example:**
```bash ```bash
# Create issue via CLI # Create issue via pipe (recommended)
ccw issue create --data '{ 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", "title": "Login fails with special chars",
"context": "500 error when password contains quotes", "context": "500 error when password contains \"quotes\"",
"priority": 2, "priority": 2,
"source": "text", "source": "text",
"expected_behavior": "Login succeeds", "expected_behavior": "Login succeeds",
"actual_behavior": "500 Internal Server Error" "actual_behavior": "500 Internal Server Error"
}' }
EOF
# Output (JSON) # Output (JSON)
{ {

View File

@@ -617,17 +617,30 @@ function generateQueueItemId(queue: Queue, level: 'solution' | 'task' = 'solutio
/** /**
* create - Create issue from JSON data * create - Create issue from JSON data
* Usage: ccw issue create --data '{"title":"...", "context":"..."}' * Usage: ccw issue create --data '{"title":"...", "context":"..."}'
* echo '{"title":"..."}' | ccw issue create
* Output: JSON with created issue (includes auto-generated ID) * Output: JSON with created issue (includes auto-generated ID)
*/ */
async function createAction(options: IssueOptions): Promise<void> { async function createAction(options: IssueOptions): Promise<void> {
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.red('JSON data required'));
console.error(chalk.gray('Usage: ccw issue create --data \'{"title":"...", "context":"..."}\'')); console.error(chalk.gray('Usage: ccw issue create --data \'{"title":"...", "context":"..."}\''));
console.error(chalk.gray(' echo \'{"title":"..."}\' | ccw issue create'));
process.exit(1); process.exit(1);
} }
try { try {
const data = JSON.parse(options.data); const data = JSON.parse(jsonData);
const issue = createIssue(data); const issue = createIssue(data);
console.log(JSON.stringify(issue, null, 2)); console.log(JSON.stringify(issue, null, 2));
} catch (err) { } catch (err) {
@@ -639,23 +652,37 @@ async function createAction(options: IssueOptions): Promise<void> {
/** /**
* solution - Create solution from JSON data * solution - Create solution from JSON data
* Usage: ccw issue solution <issue-id> --data '{"tasks":[...]}' * Usage: ccw issue solution <issue-id> --data '{"tasks":[...]}'
* echo '{"tasks":[...]}' | ccw issue solution <issue-id>
* Output: JSON with created solution (includes auto-generated ID) * Output: JSON with created solution (includes auto-generated ID)
*/ */
async function solutionAction(issueId: string | undefined, options: IssueOptions): Promise<void> { async function solutionAction(issueId: string | undefined, options: IssueOptions): Promise<void> {
if (!issueId) { if (!issueId) {
console.error(chalk.red('Issue ID required')); console.error(chalk.red('Issue ID required'));
console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\'')); console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\''));
console.error(chalk.gray(' echo \'{"tasks":[...]}\' | ccw issue solution <issue-id>'));
process.exit(1); 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.red('JSON data required'));
console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\'')); console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\''));
console.error(chalk.gray(' echo \'{"tasks":[...]}\' | ccw issue solution <issue-id>'));
process.exit(1); process.exit(1);
} }
try { try {
const data = JSON.parse(options.data); const data = JSON.parse(jsonData);
const solution = createSolution(issueId, data); const solution = createSolution(issueId, data);
console.log(JSON.stringify(solution, null, 2)); console.log(JSON.stringify(solution, null, 2));
} catch (err) { } catch (err) {