mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat(issue): add solution endpoint with auto-increment ID
- Add `ccw issue solution <id> --data '{...}'` for solution creation
- Add createSolution() with proper JSONL handling (trailing newline)
- Fix writeSolutions() to always add trailing newline
- Update plan.md to use CLI endpoint
Supports multiple solutions per issue with sequential IDs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -176,13 +176,22 @@ ${issueList}
|
||||
4. Plan solution with tasks (see issue-plan-agent.md for details)
|
||||
5. Write solutions to JSONL, bind if single solution
|
||||
|
||||
### Generate Files
|
||||
\`.workflow/issues/solutions/{issue-id}.jsonl\` - Solution with tasks (schema: cat .claude/workflows/cli-templates/schemas/solution-schema.json)
|
||||
### Solution Creation (via CLI endpoint)
|
||||
```bash
|
||||
ccw issue solution <issue-id> --data '{"description":"...", "approach":"...", "tasks":[...]}'
|
||||
```
|
||||
|
||||
**Solution ID Format**: \`SOL-{issue-id}-{seq}\` (e.g., \`SOL-GH-123-1\`, \`SOL-ISS-20251229-1\`)
|
||||
**CLI Endpoint Features:**
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| Auto-increment ID | `SOL-{issue-id}-{seq}` (e.g., `SOL-GH-123-1`) |
|
||||
| Multi-solution | Appends to existing JSONL, supports multiple per issue |
|
||||
| JSON output | Returns created solution with ID |
|
||||
|
||||
**Schema Reference:** `cat .claude/workflows/cli-templates/schemas/solution-schema.json`
|
||||
|
||||
### Binding Rules
|
||||
- **Single solution**: Auto-bind via \`ccw issue bind <id> --solution <file>\`
|
||||
- **Single solution**: Auto-bind via `ccw issue bind <issue-id> <solution-id>`
|
||||
- **Multiple solutions**: Register only, return for user selection
|
||||
|
||||
### Return Summary
|
||||
|
||||
@@ -333,7 +333,9 @@ function readSolutions(issueId: string): Solution[] {
|
||||
function writeSolutions(issueId: string, solutions: Solution[]): void {
|
||||
const dir = join(getIssuesDir(), 'solutions');
|
||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(getSolutionsPath(issueId), solutions.map(s => JSON.stringify(s)).join('\n'), 'utf-8');
|
||||
// Always add trailing newline for proper JSONL format
|
||||
const content = solutions.map(s => JSON.stringify(s)).join('\n');
|
||||
writeFileSync(getSolutionsPath(issueId), content ? content + '\n' : '', 'utf-8');
|
||||
}
|
||||
|
||||
function findSolution(issueId: string, solutionId: string): Solution | undefined {
|
||||
@@ -363,6 +365,40 @@ function generateSolutionId(issueId: string, existingSolutions: Solution[] = [])
|
||||
return `SOL-${issueId}-${maxSeq + 1}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new solution with proper JSONL handling
|
||||
* Auto-generates ID if not provided
|
||||
*/
|
||||
function createSolution(issueId: string, data: Partial<Solution>): Solution {
|
||||
const issue = findIssue(issueId);
|
||||
if (!issue) {
|
||||
throw new Error(`Issue "${issueId}" not found`);
|
||||
}
|
||||
|
||||
const solutions = readSolutions(issueId);
|
||||
const solutionId = data.id || generateSolutionId(issueId, solutions);
|
||||
|
||||
if (solutions.some(s => s.id === solutionId)) {
|
||||
throw new Error(`Solution "${solutionId}" already exists`);
|
||||
}
|
||||
|
||||
const newSolution: Solution = {
|
||||
id: solutionId,
|
||||
description: data.description || '',
|
||||
approach: data.approach || '',
|
||||
tasks: data.tasks || [],
|
||||
exploration_context: data.exploration_context,
|
||||
analysis: data.analysis,
|
||||
score: data.score,
|
||||
is_bound: false,
|
||||
created_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
solutions.push(newSolution);
|
||||
writeSolutions(issueId, solutions);
|
||||
return newSolution;
|
||||
}
|
||||
|
||||
// ============ Queue Management (Multi-Queue) ============
|
||||
|
||||
function getQueuesDir(): string {
|
||||
@@ -542,6 +578,34 @@ async function createAction(options: IssueOptions): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* solution - Create solution from JSON data
|
||||
* Usage: ccw issue solution <issue-id> --data '{"tasks":[...]}'
|
||||
* Output: JSON with created solution (includes auto-generated ID)
|
||||
*/
|
||||
async function solutionAction(issueId: string | undefined, options: IssueOptions): Promise<void> {
|
||||
if (!issueId) {
|
||||
console.error(chalk.red('Issue ID required'));
|
||||
console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\''));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!options.data) {
|
||||
console.error(chalk.red('JSON data required'));
|
||||
console.error(chalk.gray('Usage: ccw issue solution <issue-id> --data \'{"tasks":[...]}\''));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(options.data);
|
||||
const solution = createSolution(issueId, data);
|
||||
console.log(JSON.stringify(solution, null, 2));
|
||||
} catch (err) {
|
||||
console.error(chalk.red((err as Error).message));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init - Initialize a new issue (manual ID)
|
||||
*/
|
||||
@@ -1639,6 +1703,9 @@ export async function issueCommand(
|
||||
case 'create':
|
||||
await createAction(options);
|
||||
break;
|
||||
case 'solution':
|
||||
await solutionAction(argsArray[0], options);
|
||||
break;
|
||||
case 'init':
|
||||
await initAction(argsArray[0], options);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user