mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-06 01:54:11 +08:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b19edd2de | ||
|
|
3e54b5f7d8 | ||
|
|
4da06864f8 | ||
|
|
8f310339df | ||
|
|
0157e36344 | ||
|
|
cdf4833977 | ||
|
|
c8a914aeca | ||
|
|
a5ba7c0f6c | ||
|
|
1cf0d92ec2 | ||
|
|
02930bd56b | ||
|
|
4061ae48c4 | ||
|
|
ecd5085e51 | ||
|
|
6bc8b7de95 | ||
|
|
e79e33773f | ||
|
|
0c0301d811 | ||
|
|
89f6ac6804 | ||
|
|
f14c3299bc | ||
|
|
a73828b4d6 | ||
|
|
6244bf0405 | ||
|
|
90852c7788 | ||
|
|
3b842ed290 | ||
|
|
673e1d117a | ||
|
|
f64f619713 | ||
|
|
a742fa0f8a | ||
|
|
6894c7e80b | ||
|
|
203100431b | ||
|
|
e8b9bcae92 | ||
|
|
052351ab5b | ||
|
|
9dd84e3416 | ||
|
|
211c25d969 | ||
|
|
275684d319 | ||
|
|
0f8a47e8f6 | ||
|
|
303c840464 | ||
|
|
b15008fbce | ||
|
|
a8cf3e1ad6 | ||
|
|
0515ef6e8b | ||
|
|
777d5df573 | ||
|
|
c5f379ba01 | ||
|
|
145d38c9bd | ||
|
|
eab957ce00 | ||
|
|
b5fb077ad6 | ||
|
|
ebcbb11cb2 | ||
|
|
a1413dd1b3 | ||
|
|
4e6ee2db25 | ||
|
|
8e744597d1 | ||
|
|
dfa8b541b4 | ||
|
|
1dc55f8811 | ||
|
|
501d9a05d4 | ||
|
|
229d51cd18 | ||
|
|
40e61b30d6 | ||
|
|
3c3ce55842 | ||
|
|
e3e61bcae9 | ||
|
|
dfca4d60ee | ||
|
|
e671b45948 | ||
|
|
b00113d212 | ||
|
|
9b926d1a1e | ||
|
|
98c9f1a830 | ||
|
|
46ac591fe8 | ||
|
|
bf66b095c7 | ||
|
|
5228581324 | ||
|
|
c9c704e671 | ||
|
|
16d4c7c646 | ||
|
|
39056292b7 | ||
|
|
87ffd283ce | ||
|
|
8eb42816f1 | ||
|
|
ebdf64c0b9 | ||
|
|
caab5f476e | ||
|
|
1998f3ae8a | ||
|
|
5ff2a43b70 | ||
|
|
3cd842ca1a | ||
|
|
86cefa7bda | ||
|
|
fdac697f6e | ||
|
|
8203d690cb | ||
|
|
cf58dc0dd3 | ||
|
|
6a69af3bf1 | ||
|
|
acdfbb4644 | ||
|
|
72f24bf535 | ||
|
|
ba23244876 | ||
|
|
624f9f18b4 | ||
|
|
17002345c9 | ||
|
|
f3f2051c45 | ||
|
|
e60d793c8c |
@@ -2,5 +2,32 @@
|
||||
|
||||
- **CLI Tools Usage**: @~/.claude/workflows/cli-tools-usage.md
|
||||
- **Coding Philosophy**: @~/.claude/workflows/coding-philosophy.md
|
||||
- **Context Requirements**: @~/.claude/workflows/context-tools.md
|
||||
- **File Modification**: @~/.claude/workflows/file-modification.md
|
||||
- **Context Requirements**: @~/.claude/workflows/context-tools-ace.md
|
||||
- **File Modification**: @~/.claude/workflows/file-modification.md
|
||||
- **CLI Endpoints Config**: @.claude/cli-tools.json
|
||||
|
||||
## CLI Endpoints
|
||||
|
||||
**Strictly follow the @.claude/cli-tools.json configuration**
|
||||
|
||||
Available CLI endpoints are dynamically defined by the config file:
|
||||
- Built-in tools and their enable/disable status
|
||||
- Custom API endpoints registered via the Dashboard
|
||||
- Managed through the CCW Dashboard Status page
|
||||
|
||||
## Tool Execution
|
||||
|
||||
### Agent Calls
|
||||
- **Always use `run_in_background: false`** for Task tool agent calls: `Task({ subagent_type: "xxx", prompt: "...", run_in_background: false })` to ensure synchronous execution and immediate result visibility
|
||||
- **TaskOutput usage**: Only use `TaskOutput({ task_id: "xxx", block: false })` + sleep loop to poll completion status. NEVER read intermediate output during agent/CLI execution - wait for final result only
|
||||
|
||||
### CLI Tool Calls (ccw cli)
|
||||
- **Always use `run_in_background: true`** for Bash tool when calling ccw cli:
|
||||
```
|
||||
Bash({ command: "ccw cli -p '...' --tool gemini", run_in_background: true })
|
||||
```
|
||||
- **After CLI call**: If no other tasks, stop immediately - let CLI execute in background, do NOT poll with TaskOutput
|
||||
|
||||
## Code Diagnostics
|
||||
|
||||
- **Prefer `mcp__ide__getDiagnostics`** for code error checking over shell-based TypeScript compilation
|
||||
|
||||
859
.claude/agents/issue-plan-agent.md
Normal file
859
.claude/agents/issue-plan-agent.md
Normal file
@@ -0,0 +1,859 @@
|
||||
---
|
||||
name: issue-plan-agent
|
||||
description: |
|
||||
Closed-loop issue planning agent combining ACE exploration and solution generation.
|
||||
Orchestrates 4-phase workflow: Issue Understanding → ACE Exploration → Solution Planning → Validation & Output
|
||||
|
||||
Core capabilities:
|
||||
- ACE semantic search for intelligent code discovery
|
||||
- Batch processing (1-3 issues per invocation)
|
||||
- Solution JSON generation with task breakdown
|
||||
- Cross-issue conflict detection
|
||||
- Dependency mapping and DAG validation
|
||||
color: green
|
||||
---
|
||||
|
||||
You are a specialized issue planning agent that combines exploration and planning into a single closed-loop workflow for issue resolution. You produce complete, executable solutions for GitHub issues or feature requests.
|
||||
|
||||
## Input Context
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Required
|
||||
issues: [
|
||||
{
|
||||
id: string, // Issue ID (e.g., "GH-123")
|
||||
title: string, // Issue title
|
||||
description: string, // Issue description
|
||||
context: string // Additional context from context.md
|
||||
}
|
||||
],
|
||||
project_root: string, // Project root path for ACE search
|
||||
|
||||
// Optional
|
||||
batch_size: number, // Max issues per batch (default: 3)
|
||||
schema_path: string // Solution schema reference
|
||||
}
|
||||
```
|
||||
|
||||
## Schema-Driven Output
|
||||
|
||||
**CRITICAL**: Read the solution schema first to determine output structure:
|
||||
|
||||
```javascript
|
||||
// Step 1: Always read schema first
|
||||
const schema = Read('.claude/workflows/cli-templates/schemas/solution-schema.json')
|
||||
|
||||
// Step 2: Generate solution conforming to schema
|
||||
const solution = generateSolutionFromSchema(schema, explorationContext)
|
||||
```
|
||||
|
||||
## 4-Phase Execution Workflow
|
||||
|
||||
```
|
||||
Phase 1: Issue Understanding (5%)
|
||||
↓ Parse issues, extract requirements, determine complexity
|
||||
Phase 2: ACE Exploration (30%)
|
||||
↓ Semantic search, pattern discovery, dependency mapping
|
||||
Phase 3: Solution Planning (50%)
|
||||
↓ Task decomposition, implementation steps, acceptance criteria
|
||||
Phase 4: Validation & Output (15%)
|
||||
↓ DAG validation, conflict detection, solution registration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Issue Understanding
|
||||
|
||||
**Extract from each issue**:
|
||||
- Title and description analysis
|
||||
- Key requirements and constraints
|
||||
- Scope identification (files, modules, features)
|
||||
- Complexity determination
|
||||
|
||||
```javascript
|
||||
function analyzeIssue(issue) {
|
||||
return {
|
||||
issue_id: issue.id,
|
||||
requirements: extractRequirements(issue.description),
|
||||
constraints: extractConstraints(issue.context),
|
||||
scope: inferScope(issue.title, issue.description),
|
||||
complexity: determineComplexity(issue) // Low | Medium | High
|
||||
}
|
||||
}
|
||||
|
||||
function determineComplexity(issue) {
|
||||
const keywords = issue.description.toLowerCase()
|
||||
if (keywords.includes('simple') || keywords.includes('single file')) return 'Low'
|
||||
if (keywords.includes('refactor') || keywords.includes('architecture')) return 'High'
|
||||
return 'Medium'
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity Rules**:
|
||||
| Complexity | Files Affected | Task Count |
|
||||
|------------|----------------|------------|
|
||||
| Low | 1-2 files | 1-3 tasks |
|
||||
| Medium | 3-5 files | 3-6 tasks |
|
||||
| High | 6+ files | 5-10 tasks |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: ACE Exploration
|
||||
|
||||
### ACE Semantic Search (PRIMARY)
|
||||
|
||||
```javascript
|
||||
// For each issue, perform semantic search
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: project_root,
|
||||
query: `Find code related to: ${issue.title}. ${issue.description}. Keywords: ${extractKeywords(issue)}`
|
||||
})
|
||||
```
|
||||
|
||||
### Exploration Checklist
|
||||
|
||||
For each issue:
|
||||
- [ ] Identify relevant files (direct matches)
|
||||
- [ ] Find related patterns (how similar features are implemented)
|
||||
- [ ] Map integration points (where new code connects)
|
||||
- [ ] Discover dependencies (internal and external)
|
||||
- [ ] Locate test patterns (how to test this)
|
||||
|
||||
### Search Patterns
|
||||
|
||||
```javascript
|
||||
// Pattern 1: Feature location
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: project_root,
|
||||
query: "Where is user authentication implemented? Keywords: auth, login, jwt, session"
|
||||
})
|
||||
|
||||
// Pattern 2: Similar feature discovery
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: project_root,
|
||||
query: "How are API routes protected? Find middleware patterns. Keywords: middleware, router, protect"
|
||||
})
|
||||
|
||||
// Pattern 3: Integration points
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: project_root,
|
||||
query: "Where do I add new middleware to the Express app? Keywords: app.use, router.use, middleware"
|
||||
})
|
||||
|
||||
// Pattern 4: Testing patterns
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: project_root,
|
||||
query: "How are API endpoints tested? Keywords: test, jest, supertest, api"
|
||||
})
|
||||
```
|
||||
|
||||
### Exploration Output
|
||||
|
||||
```javascript
|
||||
function buildExplorationResult(aceResults, issue) {
|
||||
return {
|
||||
issue_id: issue.id,
|
||||
relevant_files: aceResults.files.map(f => ({
|
||||
path: f.path,
|
||||
relevance: f.score > 0.8 ? 'high' : f.score > 0.5 ? 'medium' : 'low',
|
||||
rationale: f.summary
|
||||
})),
|
||||
modification_points: identifyModificationPoints(aceResults),
|
||||
patterns: extractPatterns(aceResults),
|
||||
dependencies: extractDependencies(aceResults),
|
||||
test_patterns: findTestPatterns(aceResults),
|
||||
risks: identifyRisks(aceResults)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fallback Chain
|
||||
|
||||
```javascript
|
||||
// ACE → ripgrep → Glob fallback
|
||||
async function explore(issue, projectRoot) {
|
||||
try {
|
||||
return await mcp__ace-tool__search_context({
|
||||
project_root_path: projectRoot,
|
||||
query: buildQuery(issue)
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn('ACE search failed, falling back to ripgrep')
|
||||
return await ripgrepFallback(issue, projectRoot)
|
||||
}
|
||||
}
|
||||
|
||||
async function ripgrepFallback(issue, projectRoot) {
|
||||
const keywords = extractKeywords(issue)
|
||||
const results = []
|
||||
for (const keyword of keywords) {
|
||||
const matches = Bash(`rg "${keyword}" --type ts --type js -l`)
|
||||
results.push(...matches.split('\n').filter(Boolean))
|
||||
}
|
||||
return { files: [...new Set(results)] }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Solution Planning
|
||||
|
||||
### Task Decomposition (Closed-Loop)
|
||||
|
||||
```javascript
|
||||
function decomposeTasks(issue, exploration) {
|
||||
const tasks = []
|
||||
let taskId = 1
|
||||
|
||||
// Group modification points by logical unit
|
||||
const groups = groupModificationPoints(exploration.modification_points)
|
||||
|
||||
for (const group of groups) {
|
||||
tasks.push({
|
||||
id: `T${taskId++}`,
|
||||
title: group.title,
|
||||
scope: group.scope,
|
||||
action: inferAction(group),
|
||||
description: group.description,
|
||||
modification_points: group.points,
|
||||
|
||||
// Phase 1: Implementation
|
||||
implementation: generateImplementationSteps(group, exploration),
|
||||
|
||||
// Phase 2: Test
|
||||
test: generateTestRequirements(group, exploration, issue.lifecycle_requirements),
|
||||
|
||||
// Phase 3: Regression
|
||||
regression: generateRegressionChecks(group, issue.lifecycle_requirements),
|
||||
|
||||
// Phase 4: Acceptance
|
||||
acceptance: generateAcceptanceCriteria(group),
|
||||
|
||||
// Phase 5: Commit
|
||||
commit: generateCommitSpec(group, issue),
|
||||
|
||||
depends_on: inferDependencies(group, tasks),
|
||||
estimated_minutes: estimateTime(group),
|
||||
executor: inferExecutor(group)
|
||||
})
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
function generateTestRequirements(group, exploration, lifecycle) {
|
||||
const test = {
|
||||
unit: [],
|
||||
integration: [],
|
||||
commands: [],
|
||||
coverage_target: 80
|
||||
}
|
||||
|
||||
// Generate unit test requirements based on action
|
||||
if (group.action === 'Create' || group.action === 'Implement') {
|
||||
test.unit.push(`Test ${group.title} happy path`)
|
||||
test.unit.push(`Test ${group.title} error cases`)
|
||||
}
|
||||
|
||||
// Generate test commands based on project patterns
|
||||
if (exploration.test_patterns?.includes('jest')) {
|
||||
test.commands.push(`npm test -- --grep '${group.scope}'`)
|
||||
} else if (exploration.test_patterns?.includes('vitest')) {
|
||||
test.commands.push(`npx vitest run ${group.scope}`)
|
||||
} else {
|
||||
test.commands.push(`npm test`)
|
||||
}
|
||||
|
||||
// Add integration tests if needed
|
||||
if (lifecycle?.test_strategy === 'integration' || lifecycle?.test_strategy === 'e2e') {
|
||||
test.integration.push(`Integration test for ${group.title}`)
|
||||
}
|
||||
|
||||
return test
|
||||
}
|
||||
|
||||
function generateRegressionChecks(group, lifecycle) {
|
||||
const regression = []
|
||||
|
||||
switch (lifecycle?.regression_scope) {
|
||||
case 'full':
|
||||
regression.push('npm test')
|
||||
regression.push('npm run test:integration')
|
||||
break
|
||||
case 'related':
|
||||
regression.push(`npm test -- --grep '${group.scope}'`)
|
||||
regression.push(`npm test -- --changed`)
|
||||
break
|
||||
case 'affected':
|
||||
default:
|
||||
regression.push(`npm test -- --findRelatedTests ${group.points[0]?.file}`)
|
||||
break
|
||||
}
|
||||
|
||||
return regression
|
||||
}
|
||||
|
||||
function generateCommitSpec(group, issue) {
|
||||
const typeMap = {
|
||||
'Create': 'feat',
|
||||
'Implement': 'feat',
|
||||
'Update': 'feat',
|
||||
'Fix': 'fix',
|
||||
'Refactor': 'refactor',
|
||||
'Test': 'test',
|
||||
'Configure': 'chore',
|
||||
'Delete': 'chore'
|
||||
}
|
||||
|
||||
const scope = group.scope.split('/').pop()?.replace(/\..*$/, '') || 'core'
|
||||
|
||||
return {
|
||||
type: typeMap[group.action] || 'feat',
|
||||
scope: scope,
|
||||
message_template: `${typeMap[group.action] || 'feat'}(${scope}): ${group.title.toLowerCase()}\n\n${group.description || ''}`,
|
||||
breaking: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Action Type Inference
|
||||
|
||||
```javascript
|
||||
function inferAction(group) {
|
||||
const actionMap = {
|
||||
'new file': 'Create',
|
||||
'create': 'Create',
|
||||
'add': 'Implement',
|
||||
'implement': 'Implement',
|
||||
'modify': 'Update',
|
||||
'update': 'Update',
|
||||
'refactor': 'Refactor',
|
||||
'config': 'Configure',
|
||||
'test': 'Test',
|
||||
'fix': 'Fix',
|
||||
'remove': 'Delete',
|
||||
'delete': 'Delete'
|
||||
}
|
||||
|
||||
for (const [keyword, action] of Object.entries(actionMap)) {
|
||||
if (group.description.toLowerCase().includes(keyword)) {
|
||||
return action
|
||||
}
|
||||
}
|
||||
return 'Implement'
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency Analysis
|
||||
|
||||
```javascript
|
||||
function inferDependencies(currentTask, existingTasks) {
|
||||
const deps = []
|
||||
|
||||
// Rule 1: Update depends on Create for same file
|
||||
for (const task of existingTasks) {
|
||||
if (task.action === 'Create' && currentTask.action !== 'Create') {
|
||||
const taskFiles = task.modification_points.map(mp => mp.file)
|
||||
const currentFiles = currentTask.modification_points.map(mp => mp.file)
|
||||
if (taskFiles.some(f => currentFiles.includes(f))) {
|
||||
deps.push(task.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 2: Test depends on implementation
|
||||
if (currentTask.action === 'Test') {
|
||||
const testTarget = currentTask.scope.replace(/__tests__|tests?|spec/gi, '')
|
||||
for (const task of existingTasks) {
|
||||
if (task.scope.includes(testTarget) && ['Create', 'Implement', 'Update'].includes(task.action)) {
|
||||
deps.push(task.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(deps)]
|
||||
}
|
||||
|
||||
function validateDAG(tasks) {
|
||||
const graph = new Map(tasks.map(t => [t.id, t.depends_on || []]))
|
||||
const visited = new Set()
|
||||
const stack = new Set()
|
||||
|
||||
function hasCycle(taskId) {
|
||||
if (stack.has(taskId)) return true
|
||||
if (visited.has(taskId)) return false
|
||||
|
||||
visited.add(taskId)
|
||||
stack.add(taskId)
|
||||
|
||||
for (const dep of graph.get(taskId) || []) {
|
||||
if (hasCycle(dep)) return true
|
||||
}
|
||||
|
||||
stack.delete(taskId)
|
||||
return false
|
||||
}
|
||||
|
||||
for (const taskId of graph.keys()) {
|
||||
if (hasCycle(taskId)) {
|
||||
return { valid: false, error: `Circular dependency detected involving ${taskId}` }
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true }
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Steps Generation
|
||||
|
||||
```javascript
|
||||
function generateImplementationSteps(group, exploration) {
|
||||
const steps = []
|
||||
|
||||
// Step 1: Setup/Preparation
|
||||
if (group.action === 'Create') {
|
||||
steps.push(`Create ${group.scope} file structure`)
|
||||
} else {
|
||||
steps.push(`Locate ${group.points[0].target} in ${group.points[0].file}`)
|
||||
}
|
||||
|
||||
// Step 2-N: Core implementation based on patterns
|
||||
if (exploration.patterns) {
|
||||
steps.push(`Follow pattern: ${exploration.patterns}`)
|
||||
}
|
||||
|
||||
// Add modification-specific steps
|
||||
for (const point of group.points) {
|
||||
steps.push(`${point.change} at ${point.target}`)
|
||||
}
|
||||
|
||||
// Final step: Integration
|
||||
steps.push('Add error handling and edge cases')
|
||||
steps.push('Update imports and exports as needed')
|
||||
|
||||
return steps.slice(0, 7) // Max 7 steps
|
||||
}
|
||||
```
|
||||
|
||||
### Acceptance Criteria Generation (Closed-Loop)
|
||||
|
||||
```javascript
|
||||
function generateAcceptanceCriteria(task) {
|
||||
const acceptance = {
|
||||
criteria: [],
|
||||
verification: [],
|
||||
manual_checks: []
|
||||
}
|
||||
|
||||
// Action-specific criteria
|
||||
const actionCriteria = {
|
||||
'Create': [`${task.scope} file created and exports correctly`],
|
||||
'Implement': [`Feature ${task.title} works as specified`],
|
||||
'Update': [`Modified behavior matches requirements`],
|
||||
'Test': [`All test cases pass`, `Coverage >= 80%`],
|
||||
'Fix': [`Bug no longer reproducible`],
|
||||
'Configure': [`Configuration applied correctly`]
|
||||
}
|
||||
|
||||
acceptance.criteria.push(...(actionCriteria[task.action] || []))
|
||||
|
||||
// Add quantified criteria
|
||||
if (task.modification_points.length > 0) {
|
||||
acceptance.criteria.push(`${task.modification_points.length} file(s) modified correctly`)
|
||||
}
|
||||
|
||||
// Generate verification steps for each criterion
|
||||
for (const criterion of acceptance.criteria) {
|
||||
acceptance.verification.push(generateVerificationStep(criterion, task))
|
||||
}
|
||||
|
||||
// Limit to reasonable counts
|
||||
acceptance.criteria = acceptance.criteria.slice(0, 4)
|
||||
acceptance.verification = acceptance.verification.slice(0, 4)
|
||||
|
||||
return acceptance
|
||||
}
|
||||
|
||||
function generateVerificationStep(criterion, task) {
|
||||
// Generate executable verification for criterion
|
||||
if (criterion.includes('file created')) {
|
||||
return `ls -la ${task.modification_points[0]?.file} && head -20 ${task.modification_points[0]?.file}`
|
||||
}
|
||||
if (criterion.includes('test')) {
|
||||
return `npm test -- --grep '${task.scope}'`
|
||||
}
|
||||
if (criterion.includes('export')) {
|
||||
return `node -e "console.log(require('./${task.modification_points[0]?.file}'))"`
|
||||
}
|
||||
if (criterion.includes('API') || criterion.includes('endpoint')) {
|
||||
return `curl -X GET http://localhost:3000/${task.scope} -v`
|
||||
}
|
||||
// Default: describe manual check
|
||||
return `Manually verify: ${criterion}`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Validation & Output
|
||||
|
||||
### Solution Validation
|
||||
|
||||
```javascript
|
||||
function validateSolution(solution) {
|
||||
const errors = []
|
||||
|
||||
// Validate tasks
|
||||
for (const task of solution.tasks) {
|
||||
const taskErrors = validateTask(task)
|
||||
if (taskErrors.length > 0) {
|
||||
errors.push(...taskErrors.map(e => `${task.id}: ${e}`))
|
||||
}
|
||||
}
|
||||
|
||||
// Validate DAG
|
||||
const dagResult = validateDAG(solution.tasks)
|
||||
if (!dagResult.valid) {
|
||||
errors.push(dagResult.error)
|
||||
}
|
||||
|
||||
// Validate modification points exist
|
||||
for (const task of solution.tasks) {
|
||||
for (const mp of task.modification_points) {
|
||||
if (mp.target !== 'new file' && !fileExists(mp.file)) {
|
||||
errors.push(`${task.id}: File not found: ${mp.file}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: errors.length === 0, errors }
|
||||
}
|
||||
|
||||
function validateTask(task) {
|
||||
const errors = []
|
||||
|
||||
// Basic fields
|
||||
if (!/^T\d+$/.test(task.id)) errors.push('Invalid task ID format')
|
||||
if (!task.title?.trim()) errors.push('Missing title')
|
||||
if (!task.scope?.trim()) errors.push('Missing scope')
|
||||
if (!['Create', 'Update', 'Implement', 'Refactor', 'Configure', 'Test', 'Fix', 'Delete'].includes(task.action)) {
|
||||
errors.push('Invalid action type')
|
||||
}
|
||||
|
||||
// Phase 1: Implementation
|
||||
if (!task.implementation || task.implementation.length < 2) {
|
||||
errors.push('Need 2+ implementation steps')
|
||||
}
|
||||
|
||||
// Phase 2: Test
|
||||
if (!task.test) {
|
||||
errors.push('Missing test phase')
|
||||
} else {
|
||||
if (!task.test.commands || task.test.commands.length < 1) {
|
||||
errors.push('Need 1+ test commands')
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Regression
|
||||
if (!task.regression || task.regression.length < 1) {
|
||||
errors.push('Need 1+ regression checks')
|
||||
}
|
||||
|
||||
// Phase 4: Acceptance
|
||||
if (!task.acceptance) {
|
||||
errors.push('Missing acceptance phase')
|
||||
} else {
|
||||
if (!task.acceptance.criteria || task.acceptance.criteria.length < 1) {
|
||||
errors.push('Need 1+ acceptance criteria')
|
||||
}
|
||||
if (!task.acceptance.verification || task.acceptance.verification.length < 1) {
|
||||
errors.push('Need 1+ verification steps')
|
||||
}
|
||||
if (task.acceptance.criteria?.some(a => /works correctly|good performance|properly/i.test(a))) {
|
||||
errors.push('Vague acceptance criteria')
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: Commit
|
||||
if (!task.commit) {
|
||||
errors.push('Missing commit phase')
|
||||
} else {
|
||||
if (!['feat', 'fix', 'refactor', 'test', 'docs', 'chore'].includes(task.commit.type)) {
|
||||
errors.push('Invalid commit type')
|
||||
}
|
||||
if (!task.commit.scope?.trim()) {
|
||||
errors.push('Missing commit scope')
|
||||
}
|
||||
if (!task.commit.message_template?.trim()) {
|
||||
errors.push('Missing commit message template')
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
```
|
||||
|
||||
### Conflict Detection (Batch Mode)
|
||||
|
||||
```javascript
|
||||
function detectConflicts(solutions) {
|
||||
const fileModifications = new Map() // file -> [issue_ids]
|
||||
|
||||
for (const solution of solutions) {
|
||||
for (const task of solution.tasks) {
|
||||
for (const mp of task.modification_points) {
|
||||
if (!fileModifications.has(mp.file)) {
|
||||
fileModifications.set(mp.file, [])
|
||||
}
|
||||
if (!fileModifications.get(mp.file).includes(solution.issue_id)) {
|
||||
fileModifications.get(mp.file).push(solution.issue_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const conflicts = []
|
||||
for (const [file, issues] of fileModifications) {
|
||||
if (issues.length > 1) {
|
||||
conflicts.push({
|
||||
file,
|
||||
issues,
|
||||
suggested_order: suggestOrder(issues, solutions)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
||||
|
||||
function suggestOrder(issueIds, solutions) {
|
||||
// Order by: Create before Update, foundation before integration
|
||||
return issueIds.sort((a, b) => {
|
||||
const solA = solutions.find(s => s.issue_id === a)
|
||||
const solB = solutions.find(s => s.issue_id === b)
|
||||
const hasCreateA = solA.tasks.some(t => t.action === 'Create')
|
||||
const hasCreateB = solB.tasks.some(t => t.action === 'Create')
|
||||
if (hasCreateA && !hasCreateB) return -1
|
||||
if (hasCreateB && !hasCreateA) return 1
|
||||
return 0
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Output Generation
|
||||
|
||||
```javascript
|
||||
function generateOutput(solutions, conflicts) {
|
||||
return {
|
||||
solutions: solutions.map(s => ({
|
||||
issue_id: s.issue_id,
|
||||
solution: s
|
||||
})),
|
||||
conflicts,
|
||||
_metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'issue-plan-agent',
|
||||
issues_count: solutions.length,
|
||||
total_tasks: solutions.reduce((sum, s) => sum + s.tasks.length, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Solution Schema (Closed-Loop Tasks)
|
||||
|
||||
Each task MUST include ALL 5 lifecycle phases:
|
||||
|
||||
```json
|
||||
{
|
||||
"issue_id": "GH-123",
|
||||
"approach_name": "Direct Implementation",
|
||||
"summary": "Add JWT authentication middleware to protect API routes",
|
||||
"tasks": [
|
||||
{
|
||||
"id": "T1",
|
||||
"title": "Create JWT validation middleware",
|
||||
"scope": "src/middleware/",
|
||||
"action": "Create",
|
||||
"description": "Create middleware to validate JWT tokens",
|
||||
"modification_points": [
|
||||
{ "file": "src/middleware/auth.ts", "target": "new file", "change": "Create middleware" }
|
||||
],
|
||||
|
||||
"implementation": [
|
||||
"Create auth.ts file in src/middleware/",
|
||||
"Implement JWT token extraction from Authorization header",
|
||||
"Add token validation using jsonwebtoken library",
|
||||
"Handle error cases (missing, invalid, expired tokens)",
|
||||
"Export middleware function"
|
||||
],
|
||||
|
||||
"test": {
|
||||
"unit": [
|
||||
"Test valid token passes through",
|
||||
"Test invalid token returns 401",
|
||||
"Test expired token returns 401",
|
||||
"Test missing token returns 401"
|
||||
],
|
||||
"integration": [
|
||||
"Protected route returns 401 without token",
|
||||
"Protected route returns 200 with valid token"
|
||||
],
|
||||
"commands": [
|
||||
"npm test -- --grep 'auth middleware'",
|
||||
"npm run test:coverage -- src/middleware/auth.ts"
|
||||
],
|
||||
"coverage_target": 80
|
||||
},
|
||||
|
||||
"regression": [
|
||||
"npm test -- --grep 'existing routes'",
|
||||
"npm run test:integration"
|
||||
],
|
||||
|
||||
"acceptance": {
|
||||
"criteria": [
|
||||
"Middleware validates JWT tokens successfully",
|
||||
"Returns 401 with appropriate error for invalid tokens",
|
||||
"Passes decoded user payload to request context"
|
||||
],
|
||||
"verification": [
|
||||
"curl -H 'Authorization: Bearer <valid>' /api/protected → 200",
|
||||
"curl /api/protected → 401 {error: 'No token'}",
|
||||
"curl -H 'Authorization: Bearer invalid' /api/protected → 401"
|
||||
],
|
||||
"manual_checks": []
|
||||
},
|
||||
|
||||
"commit": {
|
||||
"type": "feat",
|
||||
"scope": "auth",
|
||||
"message_template": "feat(auth): add JWT validation middleware\n\n- Implement token extraction and validation\n- Add error handling for invalid/expired tokens\n- Export middleware for route protection",
|
||||
"breaking": false
|
||||
},
|
||||
|
||||
"depends_on": [],
|
||||
"estimated_minutes": 30,
|
||||
"executor": "codex"
|
||||
}
|
||||
],
|
||||
"exploration_context": {
|
||||
"relevant_files": ["src/config/env.ts"],
|
||||
"patterns": "Follow existing middleware pattern",
|
||||
"test_patterns": "Jest + supertest"
|
||||
},
|
||||
"estimated_total_minutes": 70,
|
||||
"complexity": "Medium"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```javascript
|
||||
// Error handling with fallback
|
||||
async function executeWithFallback(issue, projectRoot) {
|
||||
try {
|
||||
// Primary: ACE semantic search
|
||||
const exploration = await aceExplore(issue, projectRoot)
|
||||
return await generateSolution(issue, exploration)
|
||||
} catch (aceError) {
|
||||
console.warn('ACE failed:', aceError.message)
|
||||
|
||||
try {
|
||||
// Fallback: ripgrep-based exploration
|
||||
const exploration = await ripgrepExplore(issue, projectRoot)
|
||||
return await generateSolution(issue, exploration)
|
||||
} catch (rgError) {
|
||||
// Degraded: Basic solution without exploration
|
||||
return {
|
||||
issue_id: issue.id,
|
||||
approach_name: 'Basic Implementation',
|
||||
summary: issue.title,
|
||||
tasks: [{
|
||||
id: 'T1',
|
||||
title: issue.title,
|
||||
scope: 'TBD',
|
||||
action: 'Implement',
|
||||
description: issue.description,
|
||||
modification_points: [{ file: 'TBD', target: 'TBD', change: issue.title }],
|
||||
implementation: ['Analyze requirements', 'Implement solution', 'Test and validate'],
|
||||
acceptance: ['Feature works as described'],
|
||||
depends_on: [],
|
||||
estimated_minutes: 60
|
||||
}],
|
||||
exploration_context: { relevant_files: [], patterns: 'Manual exploration required' },
|
||||
estimated_total_minutes: 60,
|
||||
complexity: 'Medium',
|
||||
_warning: 'Degraded mode - manual exploration required'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Scenario | Action |
|
||||
|----------|--------|
|
||||
| ACE search returns no results | Fallback to ripgrep, warn user |
|
||||
| Circular task dependency | Report error, suggest fix |
|
||||
| File not found in codebase | Flag as "new file", update modification_points |
|
||||
| Ambiguous requirements | Add clarification_needs to output |
|
||||
|
||||
---
|
||||
|
||||
## Quality Standards
|
||||
|
||||
### Acceptance Criteria Quality
|
||||
|
||||
| Good | Bad |
|
||||
|------|-----|
|
||||
| "3 API endpoints: GET, POST, DELETE" | "API works correctly" |
|
||||
| "Response time < 200ms p95" | "Good performance" |
|
||||
| "All 4 test cases pass" | "Tests pass" |
|
||||
| "JWT token validated with secret from env" | "Authentication works" |
|
||||
|
||||
### Task Validation Checklist
|
||||
|
||||
Before outputting solution:
|
||||
- [ ] ACE search performed for each issue
|
||||
- [ ] All modification_points verified against codebase
|
||||
- [ ] Tasks have 2+ implementation steps
|
||||
- [ ] Tasks have 1+ quantified acceptance criteria
|
||||
- [ ] Dependencies form valid DAG (no cycles)
|
||||
- [ ] Estimated time is reasonable
|
||||
|
||||
---
|
||||
|
||||
## Key Reminders
|
||||
|
||||
**ALWAYS**:
|
||||
1. Use ACE semantic search (`mcp__ace-tool__search_context`) as PRIMARY exploration tool
|
||||
2. Read schema first before generating solution output
|
||||
3. Include `depends_on` field (even if empty `[]`)
|
||||
4. Quantify acceptance criteria with specific, testable conditions
|
||||
5. Validate DAG before output (no circular dependencies)
|
||||
6. Include file:line references in modification_points where possible
|
||||
7. Detect and report cross-issue file conflicts in batch mode
|
||||
8. Include exploration_context with patterns and relevant_files
|
||||
9. **Generate ALL 5 lifecycle phases for each task**:
|
||||
- `implementation`: 2-7 concrete steps
|
||||
- `test`: unit tests, commands, coverage target
|
||||
- `regression`: regression check commands
|
||||
- `acceptance`: criteria + verification steps
|
||||
- `commit`: type, scope, message template
|
||||
10. Infer test commands from project's test framework
|
||||
11. Generate commit message following conventional commits
|
||||
|
||||
**NEVER**:
|
||||
1. Execute implementation (return plan only)
|
||||
2. Use vague acceptance criteria ("works correctly", "good performance")
|
||||
3. Create circular dependencies in task graph
|
||||
4. Skip task validation before output
|
||||
5. Omit required fields from solution schema
|
||||
6. Assume file exists without verification
|
||||
7. Generate more than 10 tasks per issue
|
||||
8. Skip ACE search (unless fallback triggered)
|
||||
9. **Omit any of the 5 lifecycle phases** (test, regression, acceptance, commit)
|
||||
10. Skip verification steps in acceptance criteria
|
||||
702
.claude/agents/issue-queue-agent.md
Normal file
702
.claude/agents/issue-queue-agent.md
Normal file
@@ -0,0 +1,702 @@
|
||||
---
|
||||
name: issue-queue-agent
|
||||
description: |
|
||||
Task ordering agent for issue queue formation with dependency analysis and conflict resolution.
|
||||
Orchestrates 4-phase workflow: Dependency Analysis → Conflict Detection → Semantic Ordering → Group Assignment
|
||||
|
||||
Core capabilities:
|
||||
- ACE semantic search for relationship discovery
|
||||
- Cross-issue dependency DAG construction
|
||||
- File modification conflict detection
|
||||
- Conflict resolution with execution ordering
|
||||
- Semantic priority calculation (0.0-1.0)
|
||||
- Parallel/Sequential group assignment
|
||||
color: orange
|
||||
---
|
||||
|
||||
You are a specialized queue formation agent that analyzes tasks from bound solutions, resolves conflicts, and produces an ordered execution queue. You focus on optimal task ordering across multiple issues.
|
||||
|
||||
## Input Context
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Required
|
||||
tasks: [
|
||||
{
|
||||
issue_id: string, // Issue ID (e.g., "GH-123")
|
||||
solution_id: string, // Solution ID (e.g., "SOL-001")
|
||||
task: {
|
||||
id: string, // Task ID (e.g., "T1")
|
||||
title: string,
|
||||
scope: string,
|
||||
action: string, // Create | Update | Implement | Refactor | Test | Fix | Delete | Configure
|
||||
modification_points: [
|
||||
{ file: string, target: string, change: string }
|
||||
],
|
||||
depends_on: string[] // Task IDs within same issue
|
||||
},
|
||||
exploration_context: object
|
||||
}
|
||||
],
|
||||
|
||||
// Optional
|
||||
project_root: string, // Project root for ACE search
|
||||
existing_conflicts: object[], // Pre-identified conflicts
|
||||
rebuild: boolean // Clear and regenerate queue
|
||||
}
|
||||
```
|
||||
|
||||
## 4-Phase Execution Workflow
|
||||
|
||||
```
|
||||
Phase 1: Dependency Analysis (20%)
|
||||
↓ Parse depends_on, build DAG, detect cycles
|
||||
Phase 2: Conflict Detection + ACE Enhancement (30%)
|
||||
↓ Identify file conflicts, ACE semantic relationship discovery
|
||||
Phase 3: Conflict Resolution (25%)
|
||||
↓ Determine execution order for conflicting tasks
|
||||
Phase 4: Semantic Ordering & Grouping (25%)
|
||||
↓ Calculate priority, topological sort, assign groups
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Dependency Analysis
|
||||
|
||||
### Build Dependency Graph
|
||||
|
||||
```javascript
|
||||
function buildDependencyGraph(tasks) {
|
||||
const taskGraph = new Map()
|
||||
const fileModifications = new Map() // file -> [taskKeys]
|
||||
|
||||
for (const item of tasks) {
|
||||
const taskKey = `${item.issue_id}:${item.task.id}`
|
||||
taskGraph.set(taskKey, {
|
||||
...item,
|
||||
key: taskKey,
|
||||
inDegree: 0,
|
||||
outEdges: []
|
||||
})
|
||||
|
||||
// Track file modifications for conflict detection
|
||||
for (const mp of item.task.modification_points || []) {
|
||||
if (!fileModifications.has(mp.file)) {
|
||||
fileModifications.set(mp.file, [])
|
||||
}
|
||||
fileModifications.get(mp.file).push(taskKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Add explicit dependency edges (within same issue)
|
||||
for (const [key, node] of taskGraph) {
|
||||
for (const dep of node.task.depends_on || []) {
|
||||
const depKey = `${node.issue_id}:${dep}`
|
||||
if (taskGraph.has(depKey)) {
|
||||
taskGraph.get(depKey).outEdges.push(key)
|
||||
node.inDegree++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { taskGraph, fileModifications }
|
||||
}
|
||||
```
|
||||
|
||||
### Cycle Detection
|
||||
|
||||
```javascript
|
||||
function detectCycles(taskGraph) {
|
||||
const visited = new Set()
|
||||
const stack = new Set()
|
||||
const cycles = []
|
||||
|
||||
function dfs(key, path = []) {
|
||||
if (stack.has(key)) {
|
||||
// Found cycle - extract cycle path
|
||||
const cycleStart = path.indexOf(key)
|
||||
cycles.push(path.slice(cycleStart).concat(key))
|
||||
return true
|
||||
}
|
||||
if (visited.has(key)) return false
|
||||
|
||||
visited.add(key)
|
||||
stack.add(key)
|
||||
path.push(key)
|
||||
|
||||
for (const next of taskGraph.get(key)?.outEdges || []) {
|
||||
dfs(next, [...path])
|
||||
}
|
||||
|
||||
stack.delete(key)
|
||||
return false
|
||||
}
|
||||
|
||||
for (const key of taskGraph.keys()) {
|
||||
if (!visited.has(key)) {
|
||||
dfs(key)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasCycle: cycles.length > 0,
|
||||
cycles
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Conflict Detection
|
||||
|
||||
### Identify File Conflicts
|
||||
|
||||
```javascript
|
||||
function detectFileConflicts(fileModifications, taskGraph) {
|
||||
const conflicts = []
|
||||
|
||||
for (const [file, taskKeys] of fileModifications) {
|
||||
if (taskKeys.length > 1) {
|
||||
// Multiple tasks modify same file
|
||||
const taskDetails = taskKeys.map(key => {
|
||||
const node = taskGraph.get(key)
|
||||
return {
|
||||
key,
|
||||
issue_id: node.issue_id,
|
||||
task_id: node.task.id,
|
||||
title: node.task.title,
|
||||
action: node.task.action,
|
||||
scope: node.task.scope
|
||||
}
|
||||
})
|
||||
|
||||
conflicts.push({
|
||||
type: 'file_conflict',
|
||||
file,
|
||||
tasks: taskKeys,
|
||||
task_details: taskDetails,
|
||||
resolution: null,
|
||||
resolved: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
||||
```
|
||||
|
||||
### Conflict Classification
|
||||
|
||||
```javascript
|
||||
function classifyConflict(conflict, taskGraph) {
|
||||
const tasks = conflict.tasks.map(key => taskGraph.get(key))
|
||||
|
||||
// Check if all tasks are from same issue
|
||||
const isSameIssue = new Set(tasks.map(t => t.issue_id)).size === 1
|
||||
|
||||
// Check action types
|
||||
const actions = tasks.map(t => t.task.action)
|
||||
const hasCreate = actions.includes('Create')
|
||||
const hasDelete = actions.includes('Delete')
|
||||
|
||||
return {
|
||||
...conflict,
|
||||
same_issue: isSameIssue,
|
||||
has_create: hasCreate,
|
||||
has_delete: hasDelete,
|
||||
severity: hasDelete ? 'high' : hasCreate ? 'medium' : 'low'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Conflict Resolution
|
||||
|
||||
### Resolution Rules
|
||||
|
||||
| Priority | Rule | Example |
|
||||
|----------|------|---------|
|
||||
| 1 | Create before Update/Implement | T1:Create → T2:Update |
|
||||
| 2 | Foundation before integration | config/ → src/ |
|
||||
| 3 | Types before implementation | types/ → components/ |
|
||||
| 4 | Core before tests | src/ → __tests__/ |
|
||||
| 5 | Same issue order preserved | T1 → T2 → T3 |
|
||||
|
||||
### Apply Resolution Rules
|
||||
|
||||
```javascript
|
||||
function resolveConflict(conflict, taskGraph) {
|
||||
const tasks = conflict.tasks.map(key => ({
|
||||
key,
|
||||
node: taskGraph.get(key)
|
||||
}))
|
||||
|
||||
// Sort by resolution rules
|
||||
tasks.sort((a, b) => {
|
||||
const nodeA = a.node
|
||||
const nodeB = b.node
|
||||
|
||||
// Rule 1: Create before others
|
||||
if (nodeA.task.action === 'Create' && nodeB.task.action !== 'Create') return -1
|
||||
if (nodeB.task.action === 'Create' && nodeA.task.action !== 'Create') return 1
|
||||
|
||||
// Rule 2: Delete last
|
||||
if (nodeA.task.action === 'Delete' && nodeB.task.action !== 'Delete') return 1
|
||||
if (nodeB.task.action === 'Delete' && nodeA.task.action !== 'Delete') return -1
|
||||
|
||||
// Rule 3: Foundation scopes first
|
||||
const isFoundationA = isFoundationScope(nodeA.task.scope)
|
||||
const isFoundationB = isFoundationScope(nodeB.task.scope)
|
||||
if (isFoundationA && !isFoundationB) return -1
|
||||
if (isFoundationB && !isFoundationA) return 1
|
||||
|
||||
// Rule 4: Config/Types before implementation
|
||||
const isTypesA = nodeA.task.scope?.includes('types')
|
||||
const isTypesB = nodeB.task.scope?.includes('types')
|
||||
if (isTypesA && !isTypesB) return -1
|
||||
if (isTypesB && !isTypesA) return 1
|
||||
|
||||
// Rule 5: Preserve issue order (same issue)
|
||||
if (nodeA.issue_id === nodeB.issue_id) {
|
||||
return parseInt(nodeA.task.id.replace('T', '')) - parseInt(nodeB.task.id.replace('T', ''))
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
const order = tasks.map(t => t.key)
|
||||
const rationale = generateRationale(tasks)
|
||||
|
||||
return {
|
||||
...conflict,
|
||||
resolution: 'sequential',
|
||||
resolution_order: order,
|
||||
rationale,
|
||||
resolved: true
|
||||
}
|
||||
}
|
||||
|
||||
function isFoundationScope(scope) {
|
||||
if (!scope) return false
|
||||
const foundations = ['config', 'types', 'utils', 'lib', 'shared', 'common']
|
||||
return foundations.some(f => scope.toLowerCase().includes(f))
|
||||
}
|
||||
|
||||
function generateRationale(sortedTasks) {
|
||||
const reasons = []
|
||||
for (let i = 0; i < sortedTasks.length - 1; i++) {
|
||||
const curr = sortedTasks[i].node.task
|
||||
const next = sortedTasks[i + 1].node.task
|
||||
if (curr.action === 'Create') {
|
||||
reasons.push(`${curr.id} creates file before ${next.id}`)
|
||||
} else if (isFoundationScope(curr.scope)) {
|
||||
reasons.push(`${curr.id} (foundation) before ${next.id}`)
|
||||
}
|
||||
}
|
||||
return reasons.join('; ') || 'Default ordering applied'
|
||||
}
|
||||
```
|
||||
|
||||
### Apply Resolution to Graph
|
||||
|
||||
```javascript
|
||||
function applyResolutionToGraph(conflict, taskGraph) {
|
||||
const order = conflict.resolution_order
|
||||
|
||||
// Add dependency edges for sequential execution
|
||||
for (let i = 1; i < order.length; i++) {
|
||||
const prevKey = order[i - 1]
|
||||
const currKey = order[i]
|
||||
|
||||
if (taskGraph.has(prevKey) && taskGraph.has(currKey)) {
|
||||
const prevNode = taskGraph.get(prevKey)
|
||||
const currNode = taskGraph.get(currKey)
|
||||
|
||||
// Avoid duplicate edges
|
||||
if (!prevNode.outEdges.includes(currKey)) {
|
||||
prevNode.outEdges.push(currKey)
|
||||
currNode.inDegree++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Semantic Ordering & Grouping
|
||||
|
||||
### Semantic Priority Calculation
|
||||
|
||||
```javascript
|
||||
function calculateSemanticPriority(node) {
|
||||
let priority = 0.5 // Base priority
|
||||
|
||||
// Action-based priority boost
|
||||
const actionBoost = {
|
||||
'Create': 0.2,
|
||||
'Configure': 0.15,
|
||||
'Implement': 0.1,
|
||||
'Update': 0,
|
||||
'Refactor': -0.05,
|
||||
'Test': -0.1,
|
||||
'Fix': 0.05,
|
||||
'Delete': -0.15
|
||||
}
|
||||
priority += actionBoost[node.task.action] || 0
|
||||
|
||||
// Scope-based boost
|
||||
if (isFoundationScope(node.task.scope)) {
|
||||
priority += 0.1
|
||||
}
|
||||
if (node.task.scope?.includes('types')) {
|
||||
priority += 0.05
|
||||
}
|
||||
|
||||
// Clamp to [0, 1]
|
||||
return Math.max(0, Math.min(1, priority))
|
||||
}
|
||||
```
|
||||
|
||||
### Topological Sort with Priority
|
||||
|
||||
```javascript
|
||||
function topologicalSortWithPriority(taskGraph) {
|
||||
const result = []
|
||||
const queue = []
|
||||
|
||||
// Initialize with zero in-degree tasks
|
||||
for (const [key, node] of taskGraph) {
|
||||
if (node.inDegree === 0) {
|
||||
queue.push(key)
|
||||
}
|
||||
}
|
||||
|
||||
let executionOrder = 1
|
||||
while (queue.length > 0) {
|
||||
// Sort queue by semantic priority (descending)
|
||||
queue.sort((a, b) => {
|
||||
const nodeA = taskGraph.get(a)
|
||||
const nodeB = taskGraph.get(b)
|
||||
|
||||
// 1. Action priority
|
||||
const actionPriority = {
|
||||
'Create': 5, 'Configure': 4, 'Implement': 3,
|
||||
'Update': 2, 'Fix': 2, 'Refactor': 1, 'Test': 0, 'Delete': -1
|
||||
}
|
||||
const aPri = actionPriority[nodeA.task.action] ?? 2
|
||||
const bPri = actionPriority[nodeB.task.action] ?? 2
|
||||
if (aPri !== bPri) return bPri - aPri
|
||||
|
||||
// 2. Foundation scope first
|
||||
const aFound = isFoundationScope(nodeA.task.scope)
|
||||
const bFound = isFoundationScope(nodeB.task.scope)
|
||||
if (aFound !== bFound) return aFound ? -1 : 1
|
||||
|
||||
// 3. Types before implementation
|
||||
const aTypes = nodeA.task.scope?.includes('types')
|
||||
const bTypes = nodeB.task.scope?.includes('types')
|
||||
if (aTypes !== bTypes) return aTypes ? -1 : 1
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
const current = queue.shift()
|
||||
const node = taskGraph.get(current)
|
||||
node.execution_order = executionOrder++
|
||||
node.semantic_priority = calculateSemanticPriority(node)
|
||||
result.push(current)
|
||||
|
||||
// Process outgoing edges
|
||||
for (const next of node.outEdges) {
|
||||
const nextNode = taskGraph.get(next)
|
||||
nextNode.inDegree--
|
||||
if (nextNode.inDegree === 0) {
|
||||
queue.push(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for remaining nodes (cycle indication)
|
||||
if (result.length !== taskGraph.size) {
|
||||
const remaining = [...taskGraph.keys()].filter(k => !result.includes(k))
|
||||
return { success: false, error: `Unprocessed tasks: ${remaining.join(', ')}`, result }
|
||||
}
|
||||
|
||||
return { success: true, result }
|
||||
}
|
||||
```
|
||||
|
||||
### Execution Group Assignment
|
||||
|
||||
```javascript
|
||||
function assignExecutionGroups(orderedTasks, taskGraph, conflicts) {
|
||||
const groups = []
|
||||
let currentGroup = { type: 'P', number: 1, tasks: [] }
|
||||
|
||||
for (let i = 0; i < orderedTasks.length; i++) {
|
||||
const key = orderedTasks[i]
|
||||
const node = taskGraph.get(key)
|
||||
|
||||
// Determine if can run in parallel with current group
|
||||
const canParallel = canRunParallel(key, currentGroup.tasks, taskGraph, conflicts)
|
||||
|
||||
if (!canParallel && currentGroup.tasks.length > 0) {
|
||||
// Save current group and start new sequential group
|
||||
groups.push({ ...currentGroup })
|
||||
currentGroup = { type: 'S', number: groups.length + 1, tasks: [] }
|
||||
}
|
||||
|
||||
currentGroup.tasks.push(key)
|
||||
node.execution_group = `${currentGroup.type}${currentGroup.number}`
|
||||
}
|
||||
|
||||
// Save last group
|
||||
if (currentGroup.tasks.length > 0) {
|
||||
groups.push(currentGroup)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
function canRunParallel(taskKey, groupTasks, taskGraph, conflicts) {
|
||||
if (groupTasks.length === 0) return true
|
||||
|
||||
const node = taskGraph.get(taskKey)
|
||||
|
||||
// Check 1: No dependencies on group tasks
|
||||
for (const groupTask of groupTasks) {
|
||||
if (node.task.depends_on?.includes(groupTask.split(':')[1])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check 2: No file conflicts with group tasks
|
||||
for (const conflict of conflicts) {
|
||||
if (conflict.tasks.includes(taskKey)) {
|
||||
for (const groupTask of groupTasks) {
|
||||
if (conflict.tasks.includes(groupTask)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check 3: Different issues can run in parallel
|
||||
const nodeIssue = node.issue_id
|
||||
const groupIssues = new Set(groupTasks.map(t => taskGraph.get(t).issue_id))
|
||||
|
||||
return !groupIssues.has(nodeIssue)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output Generation
|
||||
|
||||
### Queue Item Format
|
||||
|
||||
```javascript
|
||||
function generateQueueItems(orderedTasks, taskGraph, conflicts) {
|
||||
const queueItems = []
|
||||
let queueIdCounter = 1
|
||||
|
||||
for (const key of orderedTasks) {
|
||||
const node = taskGraph.get(key)
|
||||
|
||||
queueItems.push({
|
||||
queue_id: `Q-${String(queueIdCounter++).padStart(3, '0')}`,
|
||||
issue_id: node.issue_id,
|
||||
solution_id: node.solution_id,
|
||||
task_id: node.task.id,
|
||||
status: 'pending',
|
||||
execution_order: node.execution_order,
|
||||
execution_group: node.execution_group,
|
||||
depends_on: mapDependenciesToQueueIds(node, queueItems),
|
||||
semantic_priority: node.semantic_priority,
|
||||
queued_at: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
|
||||
return queueItems
|
||||
}
|
||||
|
||||
function mapDependenciesToQueueIds(node, queueItems) {
|
||||
return (node.task.depends_on || []).map(dep => {
|
||||
const depKey = `${node.issue_id}:${dep}`
|
||||
const queueItem = queueItems.find(q =>
|
||||
q.issue_id === node.issue_id && q.task_id === dep
|
||||
)
|
||||
return queueItem?.queue_id || dep
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Final Output
|
||||
|
||||
```javascript
|
||||
function generateOutput(queueItems, conflicts, groups) {
|
||||
return {
|
||||
queue: queueItems,
|
||||
conflicts: conflicts.map(c => ({
|
||||
type: c.type,
|
||||
file: c.file,
|
||||
tasks: c.tasks,
|
||||
resolution: c.resolution,
|
||||
resolution_order: c.resolution_order,
|
||||
rationale: c.rationale,
|
||||
resolved: c.resolved
|
||||
})),
|
||||
execution_groups: groups.map(g => ({
|
||||
id: `${g.type}${g.number}`,
|
||||
type: g.type === 'P' ? 'parallel' : 'sequential',
|
||||
task_count: g.tasks.length,
|
||||
tasks: g.tasks
|
||||
})),
|
||||
_metadata: {
|
||||
version: '1.0',
|
||||
total_tasks: queueItems.length,
|
||||
total_conflicts: conflicts.length,
|
||||
resolved_conflicts: conflicts.filter(c => c.resolved).length,
|
||||
parallel_groups: groups.filter(g => g.type === 'P').length,
|
||||
sequential_groups: groups.filter(g => g.type === 'S').length,
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'issue-queue-agent'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```javascript
|
||||
async function executeWithValidation(tasks) {
|
||||
// Phase 1: Build graph
|
||||
const { taskGraph, fileModifications } = buildDependencyGraph(tasks)
|
||||
|
||||
// Check for cycles
|
||||
const cycleResult = detectCycles(taskGraph)
|
||||
if (cycleResult.hasCycle) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Circular dependency detected',
|
||||
cycles: cycleResult.cycles,
|
||||
suggestion: 'Remove circular dependencies or reorder tasks manually'
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Detect conflicts
|
||||
const conflicts = detectFileConflicts(fileModifications, taskGraph)
|
||||
.map(c => classifyConflict(c, taskGraph))
|
||||
|
||||
// Phase 3: Resolve conflicts
|
||||
for (const conflict of conflicts) {
|
||||
const resolved = resolveConflict(conflict, taskGraph)
|
||||
Object.assign(conflict, resolved)
|
||||
applyResolutionToGraph(conflict, taskGraph)
|
||||
}
|
||||
|
||||
// Re-check for cycles after resolution
|
||||
const postResolutionCycles = detectCycles(taskGraph)
|
||||
if (postResolutionCycles.hasCycle) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Conflict resolution created circular dependency',
|
||||
cycles: postResolutionCycles.cycles,
|
||||
suggestion: 'Manual conflict resolution required'
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Sort and group
|
||||
const sortResult = topologicalSortWithPriority(taskGraph)
|
||||
if (!sortResult.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: sortResult.error,
|
||||
partial_result: sortResult.result
|
||||
}
|
||||
}
|
||||
|
||||
const groups = assignExecutionGroups(sortResult.result, taskGraph, conflicts)
|
||||
const queueItems = generateQueueItems(sortResult.result, taskGraph, conflicts)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: generateOutput(queueItems, conflicts, groups)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Scenario | Action |
|
||||
|----------|--------|
|
||||
| Circular dependency | Report cycles, abort with suggestion |
|
||||
| Conflict resolution creates cycle | Flag for manual resolution |
|
||||
| Missing task reference in depends_on | Skip and warn |
|
||||
| Empty task list | Return empty queue |
|
||||
|
||||
---
|
||||
|
||||
## Quality Standards
|
||||
|
||||
### Ordering Validation
|
||||
|
||||
```javascript
|
||||
function validateOrdering(queueItems, taskGraph) {
|
||||
const errors = []
|
||||
|
||||
for (const item of queueItems) {
|
||||
const key = `${item.issue_id}:${item.task_id}`
|
||||
const node = taskGraph.get(key)
|
||||
|
||||
// Check dependencies come before
|
||||
for (const depQueueId of item.depends_on) {
|
||||
const depItem = queueItems.find(q => q.queue_id === depQueueId)
|
||||
if (depItem && depItem.execution_order >= item.execution_order) {
|
||||
errors.push(`${item.queue_id} ordered before dependency ${depQueueId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: errors.length === 0, errors }
|
||||
}
|
||||
```
|
||||
|
||||
### Semantic Priority Rules
|
||||
|
||||
| Factor | Priority Boost |
|
||||
|--------|---------------|
|
||||
| Create action | +0.2 |
|
||||
| Configure action | +0.15 |
|
||||
| Implement action | +0.1 |
|
||||
| Fix action | +0.05 |
|
||||
| Foundation scope (config/types/utils) | +0.1 |
|
||||
| Types scope | +0.05 |
|
||||
| Refactor action | -0.05 |
|
||||
| Test action | -0.1 |
|
||||
| Delete action | -0.15 |
|
||||
|
||||
---
|
||||
|
||||
## Key Reminders
|
||||
|
||||
**ALWAYS**:
|
||||
1. Build dependency graph before any ordering
|
||||
2. Detect cycles before and after conflict resolution
|
||||
3. Apply resolution rules consistently (Create → Update → Delete)
|
||||
4. Preserve within-issue task order when no conflicts
|
||||
5. Calculate semantic priority for all tasks
|
||||
6. Validate ordering before output
|
||||
7. Include rationale for conflict resolutions
|
||||
8. Map depends_on to queue_ids in output
|
||||
|
||||
**NEVER**:
|
||||
1. Execute tasks (ordering only)
|
||||
2. Ignore circular dependencies
|
||||
3. Create arbitrary ordering without rules
|
||||
4. Skip conflict detection
|
||||
5. Output invalid DAG
|
||||
6. Merge tasks from different issues in same parallel group if conflicts exist
|
||||
7. Assume task order without checking depends_on
|
||||
47
.claude/cli-tools.json
Normal file
47
.claude/cli-tools.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"tools": {
|
||||
"gemini": {
|
||||
"enabled": true,
|
||||
"isBuiltin": true,
|
||||
"command": "gemini",
|
||||
"description": "Google AI for code analysis"
|
||||
},
|
||||
"qwen": {
|
||||
"enabled": true,
|
||||
"isBuiltin": true,
|
||||
"command": "qwen",
|
||||
"description": "Alibaba AI assistant"
|
||||
},
|
||||
"codex": {
|
||||
"enabled": true,
|
||||
"isBuiltin": true,
|
||||
"command": "codex",
|
||||
"description": "OpenAI code generation"
|
||||
},
|
||||
"claude": {
|
||||
"enabled": true,
|
||||
"isBuiltin": true,
|
||||
"command": "claude",
|
||||
"description": "Anthropic AI assistant"
|
||||
}
|
||||
},
|
||||
"customEndpoints": [],
|
||||
"defaultTool": "gemini",
|
||||
"settings": {
|
||||
"promptFormat": "plain",
|
||||
"smartContext": {
|
||||
"enabled": false,
|
||||
"maxFiles": 10
|
||||
},
|
||||
"nativeResume": true,
|
||||
"recursiveQuery": true,
|
||||
"cache": {
|
||||
"injectionMode": "auto",
|
||||
"defaultPrefix": "",
|
||||
"defaultSuffix": ""
|
||||
},
|
||||
"codeIndexMcp": "ace"
|
||||
},
|
||||
"$schema": "./cli-tools.schema.json"
|
||||
}
|
||||
453
.claude/commands/issue/execute.md
Normal file
453
.claude/commands/issue/execute.md
Normal file
@@ -0,0 +1,453 @@
|
||||
---
|
||||
name: execute
|
||||
description: Execute queue with codex using endpoint-driven task fetching (single task per codex instance)
|
||||
argument-hint: "[--parallel <n>] [--executor codex|gemini]"
|
||||
allowed-tools: TodoWrite(*), Bash(*), Read(*), AskUserQuestion(*)
|
||||
---
|
||||
|
||||
# Issue Execute Command (/issue:execute)
|
||||
|
||||
## Overview
|
||||
|
||||
Execution orchestrator that coordinates codex instances. Each task is executed by an independent codex instance that fetches its task via CLI endpoint. **Codex does NOT read task files** - it calls `ccw issue next` to get task data dynamically.
|
||||
|
||||
**Core design:**
|
||||
- Single task per codex instance (not loop mode)
|
||||
- Endpoint-driven: `ccw issue next` → execute → `ccw issue complete`
|
||||
- No file reading in codex
|
||||
- Orchestrator manages parallelism
|
||||
|
||||
## Storage Structure (Flat JSONL)
|
||||
|
||||
```
|
||||
.workflow/issues/
|
||||
├── issues.jsonl # All issues (one per line)
|
||||
├── queue.json # Execution queue
|
||||
└── solutions/
|
||||
├── {issue-id}.jsonl # Solutions for issue
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/issue:execute [FLAGS]
|
||||
|
||||
# Examples
|
||||
/issue:execute # Execute all ready tasks
|
||||
/issue:execute --parallel 3 # Execute up to 3 tasks in parallel
|
||||
/issue:execute --executor codex # Force codex executor
|
||||
|
||||
# Flags
|
||||
--parallel <n> Max parallel codex instances (default: 1)
|
||||
--executor <type> Force executor: codex|gemini|agent
|
||||
--dry-run Show what would execute without running
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Queue Loading
|
||||
├─ Load queue.json
|
||||
├─ Count pending/ready tasks
|
||||
└─ Initialize TodoWrite tracking
|
||||
|
||||
Phase 2: Ready Task Detection
|
||||
├─ Find tasks with satisfied dependencies
|
||||
├─ Group by execution_group (parallel batches)
|
||||
└─ Determine execution order
|
||||
|
||||
Phase 3: Codex Coordination
|
||||
├─ For each ready task:
|
||||
│ ├─ Launch independent codex instance
|
||||
│ ├─ Codex calls: ccw issue next
|
||||
│ ├─ Codex receives task data (NOT file)
|
||||
│ ├─ Codex executes task
|
||||
│ ├─ Codex calls: ccw issue complete <queue-id>
|
||||
│ └─ Update TodoWrite
|
||||
└─ Parallel execution based on --parallel flag
|
||||
|
||||
Phase 4: Completion
|
||||
├─ Generate execution summary
|
||||
├─ Update issue statuses in issues.jsonl
|
||||
└─ Display results
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Queue Loading
|
||||
|
||||
```javascript
|
||||
// Load queue
|
||||
const queuePath = '.workflow/issues/queue.json';
|
||||
if (!Bash(`test -f "${queuePath}" && echo exists`).includes('exists')) {
|
||||
console.log('No queue found. Run /issue:queue first.');
|
||||
return;
|
||||
}
|
||||
|
||||
const queue = JSON.parse(Read(queuePath));
|
||||
|
||||
// Count by status
|
||||
const pending = queue.queue.filter(q => q.status === 'pending');
|
||||
const executing = queue.queue.filter(q => q.status === 'executing');
|
||||
const completed = queue.queue.filter(q => q.status === 'completed');
|
||||
|
||||
console.log(`
|
||||
## Execution Queue Status
|
||||
|
||||
- Pending: ${pending.length}
|
||||
- Executing: ${executing.length}
|
||||
- Completed: ${completed.length}
|
||||
- Total: ${queue.queue.length}
|
||||
`);
|
||||
|
||||
if (pending.length === 0 && executing.length === 0) {
|
||||
console.log('All tasks completed!');
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Ready Task Detection
|
||||
|
||||
```javascript
|
||||
// Find ready tasks (dependencies satisfied)
|
||||
function getReadyTasks() {
|
||||
const completedIds = new Set(
|
||||
queue.queue.filter(q => q.status === 'completed').map(q => q.queue_id)
|
||||
);
|
||||
|
||||
return queue.queue.filter(item => {
|
||||
if (item.status !== 'pending') return false;
|
||||
return item.depends_on.every(depId => completedIds.has(depId));
|
||||
});
|
||||
}
|
||||
|
||||
const readyTasks = getReadyTasks();
|
||||
|
||||
if (readyTasks.length === 0) {
|
||||
if (executing.length > 0) {
|
||||
console.log('Tasks are currently executing. Wait for completion.');
|
||||
} else {
|
||||
console.log('No ready tasks. Check for blocked dependencies.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${readyTasks.length} ready tasks`);
|
||||
|
||||
// Sort by execution order
|
||||
readyTasks.sort((a, b) => a.execution_order - b.execution_order);
|
||||
|
||||
// Initialize TodoWrite
|
||||
TodoWrite({
|
||||
todos: readyTasks.slice(0, parallelLimit).map(t => ({
|
||||
content: `[${t.queue_id}] ${t.issue_id}:${t.task_id}`,
|
||||
status: 'pending',
|
||||
activeForm: `Executing ${t.queue_id}`
|
||||
}))
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 3: Codex Coordination (Single Task Mode - Full Lifecycle)
|
||||
|
||||
```javascript
|
||||
// Execute tasks - single codex instance per task with full lifecycle
|
||||
async function executeTask(queueItem) {
|
||||
const codexPrompt = `
|
||||
## Single Task Execution - CLOSED-LOOP LIFECYCLE
|
||||
|
||||
You are executing ONE task from the issue queue. Each task has 5 phases that MUST ALL complete successfully.
|
||||
|
||||
### Step 1: Fetch Task
|
||||
Run this command to get your task:
|
||||
\`\`\`bash
|
||||
ccw issue next
|
||||
\`\`\`
|
||||
|
||||
This returns JSON with full lifecycle definition:
|
||||
- task.implementation: Implementation steps
|
||||
- task.test: Test requirements and commands
|
||||
- task.regression: Regression check commands
|
||||
- task.acceptance: Acceptance criteria and verification
|
||||
- task.commit: Commit specification
|
||||
|
||||
### Step 2: Execute Full Lifecycle
|
||||
|
||||
**Phase 1: IMPLEMENT**
|
||||
1. Follow task.implementation steps in order
|
||||
2. Modify files specified in modification_points
|
||||
3. Use context.relevant_files for reference
|
||||
4. Use context.patterns for code style
|
||||
|
||||
**Phase 2: TEST**
|
||||
1. Run test commands from task.test.commands
|
||||
2. Ensure all unit tests pass (task.test.unit)
|
||||
3. Run integration tests if specified (task.test.integration)
|
||||
4. Verify coverage meets task.test.coverage_target if specified
|
||||
5. If tests fail → fix code and re-run, do NOT proceed until tests pass
|
||||
|
||||
**Phase 3: REGRESSION**
|
||||
1. Run all commands in task.regression
|
||||
2. Ensure no existing tests are broken
|
||||
3. If regression fails → fix and re-run
|
||||
|
||||
**Phase 4: ACCEPTANCE**
|
||||
1. Verify each criterion in task.acceptance.criteria
|
||||
2. Execute verification steps in task.acceptance.verification
|
||||
3. Complete any manual_checks if specified
|
||||
4. All criteria MUST pass before proceeding
|
||||
|
||||
**Phase 5: COMMIT**
|
||||
1. Stage all modified files
|
||||
2. Use task.commit.message_template as commit message
|
||||
3. Commit with: git commit -m "$(cat <<'EOF'\n<message>\nEOF\n)"
|
||||
4. If commit_strategy is 'per-task', commit now
|
||||
5. If commit_strategy is 'atomic' or 'squash', stage but don't commit
|
||||
|
||||
### Step 3: Report Completion
|
||||
When ALL phases complete successfully:
|
||||
\`\`\`bash
|
||||
ccw issue complete <queue_id> --result '{
|
||||
"files_modified": ["path1", "path2"],
|
||||
"tests_passed": true,
|
||||
"regression_passed": true,
|
||||
"acceptance_passed": true,
|
||||
"committed": true,
|
||||
"commit_hash": "<hash>",
|
||||
"summary": "What was done"
|
||||
}'
|
||||
\`\`\`
|
||||
|
||||
If any phase fails and cannot be fixed:
|
||||
\`\`\`bash
|
||||
ccw issue fail <queue_id> --reason "Phase X failed: <details>"
|
||||
\`\`\`
|
||||
|
||||
### Rules
|
||||
- NEVER skip any lifecycle phase
|
||||
- Tests MUST pass before proceeding to acceptance
|
||||
- Regression MUST pass before commit
|
||||
- ALL acceptance criteria MUST be verified
|
||||
- Report accurate lifecycle status in result
|
||||
|
||||
### Start Now
|
||||
Begin by running: ccw issue next
|
||||
`;
|
||||
|
||||
// Execute codex
|
||||
const executor = queueItem.assigned_executor || flags.executor || 'codex';
|
||||
|
||||
if (executor === 'codex') {
|
||||
Bash(
|
||||
`ccw cli -p "${escapePrompt(codexPrompt)}" --tool codex --mode write --id exec-${queueItem.queue_id}`,
|
||||
timeout=3600000 // 1 hour timeout
|
||||
);
|
||||
} else if (executor === 'gemini') {
|
||||
Bash(
|
||||
`ccw cli -p "${escapePrompt(codexPrompt)}" --tool gemini --mode write --id exec-${queueItem.queue_id}`,
|
||||
timeout=1800000 // 30 min timeout
|
||||
);
|
||||
} else {
|
||||
// Agent execution
|
||||
Task(
|
||||
subagent_type="code-developer",
|
||||
run_in_background=false,
|
||||
description=`Execute ${queueItem.queue_id}`,
|
||||
prompt=codexPrompt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute with parallelism
|
||||
const parallelLimit = flags.parallel || 1;
|
||||
|
||||
for (let i = 0; i < readyTasks.length; i += parallelLimit) {
|
||||
const batch = readyTasks.slice(i, i + parallelLimit);
|
||||
|
||||
console.log(`\n### Executing Batch ${Math.floor(i / parallelLimit) + 1}`);
|
||||
console.log(batch.map(t => `- ${t.queue_id}: ${t.issue_id}:${t.task_id}`).join('\n'));
|
||||
|
||||
if (parallelLimit === 1) {
|
||||
// Sequential execution
|
||||
for (const task of batch) {
|
||||
updateTodo(task.queue_id, 'in_progress');
|
||||
await executeTask(task);
|
||||
updateTodo(task.queue_id, 'completed');
|
||||
}
|
||||
} else {
|
||||
// Parallel execution - launch all at once
|
||||
const executions = batch.map(task => {
|
||||
updateTodo(task.queue_id, 'in_progress');
|
||||
return executeTask(task);
|
||||
});
|
||||
await Promise.all(executions);
|
||||
batch.forEach(task => updateTodo(task.queue_id, 'completed'));
|
||||
}
|
||||
|
||||
// Refresh ready tasks after batch
|
||||
const newReady = getReadyTasks();
|
||||
if (newReady.length > 0) {
|
||||
console.log(`${newReady.length} more tasks now ready`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Codex Task Fetch Response
|
||||
|
||||
When codex calls `ccw issue next`, it receives:
|
||||
|
||||
```json
|
||||
{
|
||||
"queue_id": "Q-001",
|
||||
"issue_id": "GH-123",
|
||||
"solution_id": "SOL-001",
|
||||
"task": {
|
||||
"id": "T1",
|
||||
"title": "Create auth middleware",
|
||||
"scope": "src/middleware/",
|
||||
"action": "Create",
|
||||
"description": "Create JWT validation middleware",
|
||||
"modification_points": [
|
||||
{ "file": "src/middleware/auth.ts", "target": "new file", "change": "Create middleware" }
|
||||
],
|
||||
"implementation": [
|
||||
"Create auth.ts file in src/middleware/",
|
||||
"Implement JWT token validation using jsonwebtoken",
|
||||
"Add error handling for invalid/expired tokens",
|
||||
"Export middleware function"
|
||||
],
|
||||
"acceptance": [
|
||||
"Middleware validates JWT tokens successfully",
|
||||
"Returns 401 for invalid or missing tokens",
|
||||
"Passes token payload to request context"
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"relevant_files": ["src/config/auth.ts", "src/types/auth.d.ts"],
|
||||
"patterns": "Follow existing middleware pattern in src/middleware/logger.ts"
|
||||
},
|
||||
"execution_hints": {
|
||||
"executor": "codex",
|
||||
"estimated_minutes": 30
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Completion Summary
|
||||
|
||||
```javascript
|
||||
// Reload queue for final status
|
||||
const finalQueue = JSON.parse(Read(queuePath));
|
||||
|
||||
const summary = {
|
||||
completed: finalQueue.queue.filter(q => q.status === 'completed').length,
|
||||
failed: finalQueue.queue.filter(q => q.status === 'failed').length,
|
||||
pending: finalQueue.queue.filter(q => q.status === 'pending').length,
|
||||
total: finalQueue.queue.length
|
||||
};
|
||||
|
||||
console.log(`
|
||||
## Execution Complete
|
||||
|
||||
**Completed**: ${summary.completed}/${summary.total}
|
||||
**Failed**: ${summary.failed}
|
||||
**Pending**: ${summary.pending}
|
||||
|
||||
### Task Results
|
||||
${finalQueue.queue.map(q => {
|
||||
const icon = q.status === 'completed' ? '✓' :
|
||||
q.status === 'failed' ? '✗' :
|
||||
q.status === 'executing' ? '⟳' : '○';
|
||||
return `${icon} ${q.queue_id} [${q.issue_id}:${q.task_id}] - ${q.status}`;
|
||||
}).join('\n')}
|
||||
`);
|
||||
|
||||
// Update issue statuses in issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
const allIssues = Bash(`cat "${issuesPath}"`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
const issueIds = [...new Set(finalQueue.queue.map(q => q.issue_id))];
|
||||
for (const issueId of issueIds) {
|
||||
const issueTasks = finalQueue.queue.filter(q => q.issue_id === issueId);
|
||||
|
||||
if (issueTasks.every(q => q.status === 'completed')) {
|
||||
console.log(`\n✓ Issue ${issueId} fully completed!`);
|
||||
|
||||
// Update issue status
|
||||
const issueIndex = allIssues.findIndex(i => i.id === issueId);
|
||||
if (issueIndex !== -1) {
|
||||
allIssues[issueIndex].status = 'completed';
|
||||
allIssues[issueIndex].completed_at = new Date().toISOString();
|
||||
allIssues[issueIndex].updated_at = new Date().toISOString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write updated issues.jsonl
|
||||
Write(issuesPath, allIssues.map(i => JSON.stringify(i)).join('\n'));
|
||||
|
||||
if (summary.pending > 0) {
|
||||
console.log(`
|
||||
### Continue Execution
|
||||
Run \`/issue:execute\` again to execute remaining tasks.
|
||||
`);
|
||||
}
|
||||
```
|
||||
|
||||
## Dry Run Mode
|
||||
|
||||
```javascript
|
||||
if (flags.dryRun) {
|
||||
console.log(`
|
||||
## Dry Run - Would Execute
|
||||
|
||||
${readyTasks.map((t, i) => `
|
||||
${i + 1}. ${t.queue_id}
|
||||
Issue: ${t.issue_id}
|
||||
Task: ${t.task_id}
|
||||
Executor: ${t.assigned_executor}
|
||||
Group: ${t.execution_group}
|
||||
`).join('')}
|
||||
|
||||
No changes made. Remove --dry-run to execute.
|
||||
`);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Resolution |
|
||||
|-------|------------|
|
||||
| Queue not found | Display message, suggest /issue:queue |
|
||||
| No ready tasks | Check dependencies, show blocked tasks |
|
||||
| Codex timeout | Mark as failed, allow retry |
|
||||
| ccw issue next empty | All tasks done or blocked |
|
||||
| Task execution failure | Marked via ccw issue fail |
|
||||
|
||||
## Endpoint Contract
|
||||
|
||||
### `ccw issue next`
|
||||
- Returns next ready task as JSON
|
||||
- Marks task as 'executing'
|
||||
- Returns `{ status: 'empty' }` when no tasks
|
||||
|
||||
### `ccw issue complete <queue-id>`
|
||||
- Marks task as 'completed'
|
||||
- Updates queue.json
|
||||
- Checks if issue is fully complete
|
||||
|
||||
### `ccw issue fail <queue-id>`
|
||||
- Marks task as 'failed'
|
||||
- Records failure reason
|
||||
- Allows retry via /issue:execute
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/issue:plan` - Plan issues with solutions
|
||||
- `/issue:queue` - Form execution queue
|
||||
- `ccw issue queue list` - View queue status
|
||||
- `ccw issue retry` - Retry failed tasks
|
||||
865
.claude/commands/issue/manage.md
Normal file
865
.claude/commands/issue/manage.md
Normal file
@@ -0,0 +1,865 @@
|
||||
---
|
||||
name: manage
|
||||
description: Interactive issue management (CRUD) via ccw cli endpoints with menu-driven interface
|
||||
argument-hint: "[issue-id] [--action list|view|edit|delete|bulk]"
|
||||
allowed-tools: TodoWrite(*), Bash(*), Read(*), Write(*), AskUserQuestion(*), Task(*)
|
||||
---
|
||||
|
||||
# Issue Manage Command (/issue:manage)
|
||||
|
||||
## Overview
|
||||
|
||||
Interactive menu-driven interface for issue management using `ccw issue` CLI endpoints:
|
||||
- **List**: Browse and filter issues
|
||||
- **View**: Detailed issue inspection
|
||||
- **Edit**: Modify issue fields
|
||||
- **Delete**: Remove issues
|
||||
- **Bulk**: Batch operations on multiple issues
|
||||
|
||||
## CLI Endpoints Reference
|
||||
|
||||
```bash
|
||||
# Core endpoints (ccw issue)
|
||||
ccw issue list # List all issues
|
||||
ccw issue list <id> --json # Get issue details
|
||||
ccw issue status <id> # Detailed status
|
||||
ccw issue init <id> --title "..." # Create issue
|
||||
ccw issue task <id> --title "..." # Add task
|
||||
|
||||
# Queue management
|
||||
ccw issue queue # List queue
|
||||
ccw issue queue add <id> # Add to queue
|
||||
ccw issue next # Get next task
|
||||
ccw issue done <queue-id> # Complete task
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Interactive mode (menu-driven)
|
||||
/issue:manage
|
||||
|
||||
# Direct to specific issue
|
||||
/issue:manage GH-123
|
||||
|
||||
# Direct action
|
||||
/issue:manage --action list
|
||||
/issue:manage GH-123 --action edit
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Entry Point
|
||||
|
||||
```javascript
|
||||
const issueId = parseIssueId(userInput);
|
||||
const action = flags.action;
|
||||
|
||||
// Show main menu if no action specified
|
||||
if (!action) {
|
||||
await showMainMenu(issueId);
|
||||
} else {
|
||||
await executeAction(action, issueId);
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Main Menu
|
||||
|
||||
```javascript
|
||||
async function showMainMenu(preselectedIssue = null) {
|
||||
// Fetch current issues summary
|
||||
const issuesResult = Bash('ccw issue list --json 2>/dev/null || echo "[]"');
|
||||
const issues = JSON.parse(issuesResult) || [];
|
||||
|
||||
const queueResult = Bash('ccw issue status --json 2>/dev/null');
|
||||
const queueStatus = JSON.parse(queueResult || '{}');
|
||||
|
||||
console.log(`
|
||||
## Issue Management Dashboard
|
||||
|
||||
**Total Issues**: ${issues.length}
|
||||
**Queue Status**: ${queueStatus.queue?.total_tasks || 0} tasks (${queueStatus.queue?.pending_count || 0} pending)
|
||||
|
||||
### Quick Stats
|
||||
- Registered: ${issues.filter(i => i.status === 'registered').length}
|
||||
- Planned: ${issues.filter(i => i.status === 'planned').length}
|
||||
- Executing: ${issues.filter(i => i.status === 'executing').length}
|
||||
- Completed: ${issues.filter(i => i.status === 'completed').length}
|
||||
`);
|
||||
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'What would you like to do?',
|
||||
header: 'Action',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'List Issues', description: 'Browse all issues with filters' },
|
||||
{ label: 'View Issue', description: 'Detailed view of specific issue' },
|
||||
{ label: 'Create Issue', description: 'Add new issue from text or GitHub' },
|
||||
{ label: 'Edit Issue', description: 'Modify issue fields' },
|
||||
{ label: 'Delete Issue', description: 'Remove issue(s)' },
|
||||
{ label: 'Bulk Operations', description: 'Batch actions on multiple issues' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const selected = parseAnswer(answer);
|
||||
|
||||
switch (selected) {
|
||||
case 'List Issues':
|
||||
await listIssuesInteractive();
|
||||
break;
|
||||
case 'View Issue':
|
||||
await viewIssueInteractive(preselectedIssue);
|
||||
break;
|
||||
case 'Create Issue':
|
||||
await createIssueInteractive();
|
||||
break;
|
||||
case 'Edit Issue':
|
||||
await editIssueInteractive(preselectedIssue);
|
||||
break;
|
||||
case 'Delete Issue':
|
||||
await deleteIssueInteractive(preselectedIssue);
|
||||
break;
|
||||
case 'Bulk Operations':
|
||||
await bulkOperationsInteractive();
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: List Issues
|
||||
|
||||
```javascript
|
||||
async function listIssuesInteractive() {
|
||||
// Ask for filter
|
||||
const filterAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Filter issues by status?',
|
||||
header: 'Filter',
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{ label: 'All', description: 'Show all issues' },
|
||||
{ label: 'Registered', description: 'New, unplanned issues' },
|
||||
{ label: 'Planned', description: 'Issues with bound solutions' },
|
||||
{ label: 'Queued', description: 'In execution queue' },
|
||||
{ label: 'Executing', description: 'Currently being worked on' },
|
||||
{ label: 'Completed', description: 'Finished issues' },
|
||||
{ label: 'Failed', description: 'Failed issues' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const filters = parseMultiAnswer(filterAnswer);
|
||||
|
||||
// Fetch and filter issues
|
||||
const result = Bash('ccw issue list --json');
|
||||
let issues = JSON.parse(result) || [];
|
||||
|
||||
if (!filters.includes('All')) {
|
||||
const statusMap = {
|
||||
'Registered': 'registered',
|
||||
'Planned': 'planned',
|
||||
'Queued': 'queued',
|
||||
'Executing': 'executing',
|
||||
'Completed': 'completed',
|
||||
'Failed': 'failed'
|
||||
};
|
||||
const allowedStatuses = filters.map(f => statusMap[f]).filter(Boolean);
|
||||
issues = issues.filter(i => allowedStatuses.includes(i.status));
|
||||
}
|
||||
|
||||
if (issues.length === 0) {
|
||||
console.log('No issues found matching filters.');
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Display issues table
|
||||
console.log(`
|
||||
## Issues (${issues.length})
|
||||
|
||||
| ID | Status | Priority | Title |
|
||||
|----|--------|----------|-------|
|
||||
${issues.map(i => `| ${i.id} | ${i.status} | P${i.priority} | ${i.title.substring(0, 40)} |`).join('\n')}
|
||||
`);
|
||||
|
||||
// Ask for action on issue
|
||||
const actionAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select an issue to view/edit, or return to menu:',
|
||||
header: 'Select',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
...issues.slice(0, 10).map(i => ({
|
||||
label: i.id,
|
||||
description: i.title.substring(0, 50)
|
||||
})),
|
||||
{ label: 'Back to Menu', description: 'Return to main menu' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const selected = parseAnswer(actionAnswer);
|
||||
|
||||
if (selected === 'Back to Menu') {
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// View selected issue
|
||||
await viewIssueInteractive(selected);
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: View Issue
|
||||
|
||||
```javascript
|
||||
async function viewIssueInteractive(issueId) {
|
||||
if (!issueId) {
|
||||
// Ask for issue ID
|
||||
const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
|
||||
|
||||
const idAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select issue to view:',
|
||||
header: 'Issue',
|
||||
multiSelect: false,
|
||||
options: issues.slice(0, 10).map(i => ({
|
||||
label: i.id,
|
||||
description: `${i.status} - ${i.title.substring(0, 40)}`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
|
||||
issueId = parseAnswer(idAnswer);
|
||||
}
|
||||
|
||||
// Fetch detailed status
|
||||
const result = Bash(`ccw issue status ${issueId} --json`);
|
||||
const data = JSON.parse(result);
|
||||
|
||||
const issue = data.issue;
|
||||
const solutions = data.solutions || [];
|
||||
const bound = data.bound;
|
||||
|
||||
console.log(`
|
||||
## Issue: ${issue.id}
|
||||
|
||||
**Title**: ${issue.title}
|
||||
**Status**: ${issue.status}
|
||||
**Priority**: P${issue.priority}
|
||||
**Created**: ${issue.created_at}
|
||||
**Updated**: ${issue.updated_at}
|
||||
|
||||
### Context
|
||||
${issue.context || 'No context provided'}
|
||||
|
||||
### Solutions (${solutions.length})
|
||||
${solutions.length === 0 ? 'No solutions registered' :
|
||||
solutions.map(s => `- ${s.is_bound ? '◉' : '○'} ${s.id}: ${s.tasks?.length || 0} tasks`).join('\n')}
|
||||
|
||||
${bound ? `### Bound Solution: ${bound.id}\n**Tasks**: ${bound.tasks?.length || 0}` : ''}
|
||||
`);
|
||||
|
||||
// Show tasks if bound solution exists
|
||||
if (bound?.tasks?.length > 0) {
|
||||
console.log(`
|
||||
### Tasks
|
||||
| ID | Action | Scope | Title |
|
||||
|----|--------|-------|-------|
|
||||
${bound.tasks.map(t => `| ${t.id} | ${t.action} | ${t.scope?.substring(0, 20) || '-'} | ${t.title.substring(0, 30)} |`).join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
// Action menu
|
||||
const actionAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'What would you like to do?',
|
||||
header: 'Action',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Edit Issue', description: 'Modify issue fields' },
|
||||
{ label: 'Plan Issue', description: 'Generate solution (/issue:plan)' },
|
||||
{ label: 'Add to Queue', description: 'Queue bound solution tasks' },
|
||||
{ label: 'View Queue', description: 'See queue status' },
|
||||
{ label: 'Delete Issue', description: 'Remove this issue' },
|
||||
{ label: 'Back to Menu', description: 'Return to main menu' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const action = parseAnswer(actionAnswer);
|
||||
|
||||
switch (action) {
|
||||
case 'Edit Issue':
|
||||
await editIssueInteractive(issueId);
|
||||
break;
|
||||
case 'Plan Issue':
|
||||
console.log(`Running: /issue:plan ${issueId}`);
|
||||
// Invoke plan skill
|
||||
break;
|
||||
case 'Add to Queue':
|
||||
Bash(`ccw issue queue add ${issueId}`);
|
||||
console.log(`✓ Added ${issueId} tasks to queue`);
|
||||
break;
|
||||
case 'View Queue':
|
||||
const queueOutput = Bash('ccw issue queue');
|
||||
console.log(queueOutput);
|
||||
break;
|
||||
case 'Delete Issue':
|
||||
await deleteIssueInteractive(issueId);
|
||||
break;
|
||||
default:
|
||||
return showMainMenu();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Edit Issue
|
||||
|
||||
```javascript
|
||||
async function editIssueInteractive(issueId) {
|
||||
if (!issueId) {
|
||||
const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
|
||||
const idAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select issue to edit:',
|
||||
header: 'Issue',
|
||||
multiSelect: false,
|
||||
options: issues.slice(0, 10).map(i => ({
|
||||
label: i.id,
|
||||
description: `${i.status} - ${i.title.substring(0, 40)}`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
issueId = parseAnswer(idAnswer);
|
||||
}
|
||||
|
||||
// Get current issue data
|
||||
const result = Bash(`ccw issue list ${issueId} --json`);
|
||||
const issueData = JSON.parse(result);
|
||||
const issue = issueData.issue || issueData;
|
||||
|
||||
// Ask which field to edit
|
||||
const fieldAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Which field to edit?',
|
||||
header: 'Field',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Title', description: `Current: ${issue.title?.substring(0, 40)}` },
|
||||
{ label: 'Priority', description: `Current: P${issue.priority}` },
|
||||
{ label: 'Status', description: `Current: ${issue.status}` },
|
||||
{ label: 'Context', description: 'Edit problem description' },
|
||||
{ label: 'Labels', description: `Current: ${issue.labels?.join(', ') || 'none'}` },
|
||||
{ label: 'Back', description: 'Return without changes' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const field = parseAnswer(fieldAnswer);
|
||||
|
||||
if (field === 'Back') {
|
||||
return viewIssueInteractive(issueId);
|
||||
}
|
||||
|
||||
let updatePayload = {};
|
||||
|
||||
switch (field) {
|
||||
case 'Title':
|
||||
const titleAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter new title (or select current to keep):',
|
||||
header: 'Title',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: issue.title.substring(0, 50), description: 'Keep current title' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const newTitle = parseAnswer(titleAnswer);
|
||||
if (newTitle && newTitle !== issue.title.substring(0, 50)) {
|
||||
updatePayload.title = newTitle;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Priority':
|
||||
const priorityAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select priority:',
|
||||
header: 'Priority',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'P1 - Critical', description: 'Production blocking' },
|
||||
{ label: 'P2 - High', description: 'Major functionality' },
|
||||
{ label: 'P3 - Medium', description: 'Normal priority (default)' },
|
||||
{ label: 'P4 - Low', description: 'Minor issues' },
|
||||
{ label: 'P5 - Trivial', description: 'Nice to have' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const priorityStr = parseAnswer(priorityAnswer);
|
||||
updatePayload.priority = parseInt(priorityStr.charAt(1));
|
||||
break;
|
||||
|
||||
case 'Status':
|
||||
const statusAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select status:',
|
||||
header: 'Status',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'registered', description: 'New issue, not yet planned' },
|
||||
{ label: 'planning', description: 'Solution being generated' },
|
||||
{ label: 'planned', description: 'Solution bound, ready for queue' },
|
||||
{ label: 'queued', description: 'In execution queue' },
|
||||
{ label: 'executing', description: 'Currently being worked on' },
|
||||
{ label: 'completed', description: 'All tasks finished' },
|
||||
{ label: 'failed', description: 'Execution failed' },
|
||||
{ label: 'paused', description: 'Temporarily on hold' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
updatePayload.status = parseAnswer(statusAnswer);
|
||||
break;
|
||||
|
||||
case 'Context':
|
||||
console.log(`Current context:\n${issue.context || '(empty)'}\n`);
|
||||
const contextAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter new context (problem description):',
|
||||
header: 'Context',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Keep current', description: 'No changes' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const newContext = parseAnswer(contextAnswer);
|
||||
if (newContext && newContext !== 'Keep current') {
|
||||
updatePayload.context = newContext;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Labels':
|
||||
const labelsAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter labels (comma-separated):',
|
||||
header: 'Labels',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: issue.labels?.join(',') || '', description: 'Keep current labels' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const labelsStr = parseAnswer(labelsAnswer);
|
||||
if (labelsStr) {
|
||||
updatePayload.labels = labelsStr.split(',').map(l => l.trim());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply update if any
|
||||
if (Object.keys(updatePayload).length > 0) {
|
||||
// Read, update, write issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
const allIssues = Bash(`cat "${issuesPath}"`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
const idx = allIssues.findIndex(i => i.id === issueId);
|
||||
if (idx !== -1) {
|
||||
allIssues[idx] = {
|
||||
...allIssues[idx],
|
||||
...updatePayload,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
Write(issuesPath, allIssues.map(i => JSON.stringify(i)).join('\n'));
|
||||
console.log(`✓ Updated ${issueId}: ${Object.keys(updatePayload).join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Continue editing or return
|
||||
const continueAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Continue editing?',
|
||||
header: 'Continue',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Edit Another Field', description: 'Continue editing this issue' },
|
||||
{ label: 'View Issue', description: 'See updated issue' },
|
||||
{ label: 'Back to Menu', description: 'Return to main menu' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const cont = parseAnswer(continueAnswer);
|
||||
if (cont === 'Edit Another Field') {
|
||||
await editIssueInteractive(issueId);
|
||||
} else if (cont === 'View Issue') {
|
||||
await viewIssueInteractive(issueId);
|
||||
} else {
|
||||
return showMainMenu();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 6: Delete Issue
|
||||
|
||||
```javascript
|
||||
async function deleteIssueInteractive(issueId) {
|
||||
if (!issueId) {
|
||||
const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
|
||||
const idAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select issue to delete:',
|
||||
header: 'Delete',
|
||||
multiSelect: false,
|
||||
options: issues.slice(0, 10).map(i => ({
|
||||
label: i.id,
|
||||
description: `${i.status} - ${i.title.substring(0, 40)}`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
issueId = parseAnswer(idAnswer);
|
||||
}
|
||||
|
||||
// Confirm deletion
|
||||
const confirmAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Delete issue ${issueId}? This will also remove associated solutions.`,
|
||||
header: 'Confirm',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Delete', description: 'Permanently remove issue and solutions' },
|
||||
{ label: 'Cancel', description: 'Keep issue' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (parseAnswer(confirmAnswer) !== 'Delete') {
|
||||
console.log('Deletion cancelled.');
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Remove from issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
const allIssues = Bash(`cat "${issuesPath}"`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
const filtered = allIssues.filter(i => i.id !== issueId);
|
||||
Write(issuesPath, filtered.map(i => JSON.stringify(i)).join('\n'));
|
||||
|
||||
// Remove solutions file if exists
|
||||
const solPath = `.workflow/issues/solutions/${issueId}.jsonl`;
|
||||
Bash(`rm -f "${solPath}" 2>/dev/null || true`);
|
||||
|
||||
// Remove from queue if present
|
||||
const queuePath = '.workflow/issues/queue.json';
|
||||
if (Bash(`test -f "${queuePath}" && echo exists`) === 'exists') {
|
||||
const queue = JSON.parse(Bash(`cat "${queuePath}"`));
|
||||
queue.queue = queue.queue.filter(q => q.issue_id !== issueId);
|
||||
Write(queuePath, JSON.stringify(queue, null, 2));
|
||||
}
|
||||
|
||||
console.log(`✓ Deleted issue ${issueId}`);
|
||||
return showMainMenu();
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 7: Bulk Operations
|
||||
|
||||
```javascript
|
||||
async function bulkOperationsInteractive() {
|
||||
const bulkAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select bulk operation:',
|
||||
header: 'Bulk',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Update Status', description: 'Change status of multiple issues' },
|
||||
{ label: 'Update Priority', description: 'Change priority of multiple issues' },
|
||||
{ label: 'Add Labels', description: 'Add labels to multiple issues' },
|
||||
{ label: 'Delete Multiple', description: 'Remove multiple issues' },
|
||||
{ label: 'Queue All Planned', description: 'Add all planned issues to queue' },
|
||||
{ label: 'Retry All Failed', description: 'Reset all failed tasks to pending' },
|
||||
{ label: 'Back', description: 'Return to main menu' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const operation = parseAnswer(bulkAnswer);
|
||||
|
||||
if (operation === 'Back') {
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Get issues for selection
|
||||
const allIssues = JSON.parse(Bash('ccw issue list --json') || '[]');
|
||||
|
||||
if (operation === 'Queue All Planned') {
|
||||
const planned = allIssues.filter(i => i.status === 'planned' && i.bound_solution_id);
|
||||
for (const issue of planned) {
|
||||
Bash(`ccw issue queue add ${issue.id}`);
|
||||
console.log(`✓ Queued ${issue.id}`);
|
||||
}
|
||||
console.log(`\n✓ Queued ${planned.length} issues`);
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
if (operation === 'Retry All Failed') {
|
||||
Bash('ccw issue retry');
|
||||
console.log('✓ Reset all failed tasks to pending');
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Multi-select issues
|
||||
const selectAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select issues (multi-select):',
|
||||
header: 'Select',
|
||||
multiSelect: true,
|
||||
options: allIssues.slice(0, 15).map(i => ({
|
||||
label: i.id,
|
||||
description: `${i.status} - ${i.title.substring(0, 30)}`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
|
||||
const selectedIds = parseMultiAnswer(selectAnswer);
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
console.log('No issues selected.');
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Execute bulk operation
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
let issues = Bash(`cat "${issuesPath}"`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
switch (operation) {
|
||||
case 'Update Status':
|
||||
const statusAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select new status:',
|
||||
header: 'Status',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'registered', description: 'Reset to registered' },
|
||||
{ label: 'paused', description: 'Pause issues' },
|
||||
{ label: 'completed', description: 'Mark completed' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const newStatus = parseAnswer(statusAnswer);
|
||||
issues = issues.map(i =>
|
||||
selectedIds.includes(i.id)
|
||||
? { ...i, status: newStatus, updated_at: new Date().toISOString() }
|
||||
: i
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Update Priority':
|
||||
const prioAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select new priority:',
|
||||
header: 'Priority',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'P1', description: 'Critical' },
|
||||
{ label: 'P2', description: 'High' },
|
||||
{ label: 'P3', description: 'Medium' },
|
||||
{ label: 'P4', description: 'Low' },
|
||||
{ label: 'P5', description: 'Trivial' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const newPrio = parseInt(parseAnswer(prioAnswer).charAt(1));
|
||||
issues = issues.map(i =>
|
||||
selectedIds.includes(i.id)
|
||||
? { ...i, priority: newPrio, updated_at: new Date().toISOString() }
|
||||
: i
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Add Labels':
|
||||
const labelAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter labels to add (comma-separated):',
|
||||
header: 'Labels',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'bug', description: 'Bug fix' },
|
||||
{ label: 'feature', description: 'New feature' },
|
||||
{ label: 'urgent', description: 'Urgent priority' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
const newLabels = parseAnswer(labelAnswer).split(',').map(l => l.trim());
|
||||
issues = issues.map(i =>
|
||||
selectedIds.includes(i.id)
|
||||
? {
|
||||
...i,
|
||||
labels: [...new Set([...(i.labels || []), ...newLabels])],
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
: i
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Delete Multiple':
|
||||
const confirmDelete = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Delete ${selectedIds.length} issues permanently?`,
|
||||
header: 'Confirm',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Delete All', description: 'Remove selected issues' },
|
||||
{ label: 'Cancel', description: 'Keep issues' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (parseAnswer(confirmDelete) === 'Delete All') {
|
||||
issues = issues.filter(i => !selectedIds.includes(i.id));
|
||||
// Clean up solutions
|
||||
for (const id of selectedIds) {
|
||||
Bash(`rm -f ".workflow/issues/solutions/${id}.jsonl" 2>/dev/null || true`);
|
||||
}
|
||||
} else {
|
||||
console.log('Deletion cancelled.');
|
||||
return showMainMenu();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Write(issuesPath, issues.map(i => JSON.stringify(i)).join('\n'));
|
||||
console.log(`✓ Updated ${selectedIds.length} issues`);
|
||||
return showMainMenu();
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 8: Create Issue (Redirect)
|
||||
|
||||
```javascript
|
||||
async function createIssueInteractive() {
|
||||
const typeAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Create issue from:',
|
||||
header: 'Source',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'GitHub URL', description: 'Import from GitHub issue' },
|
||||
{ label: 'Text Description', description: 'Enter problem description' },
|
||||
{ label: 'Quick Create', description: 'Just title and priority' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const type = parseAnswer(typeAnswer);
|
||||
|
||||
if (type === 'GitHub URL' || type === 'Text Description') {
|
||||
console.log('Use /issue:new for structured issue creation');
|
||||
console.log('Example: /issue:new https://github.com/org/repo/issues/123');
|
||||
return showMainMenu();
|
||||
}
|
||||
|
||||
// Quick create
|
||||
const titleAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter issue title:',
|
||||
header: 'Title',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Authentication Bug', description: 'Example title' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const title = parseAnswer(titleAnswer);
|
||||
|
||||
const prioAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Select priority:',
|
||||
header: 'Priority',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'P3 - Medium (Recommended)', description: 'Normal priority' },
|
||||
{ label: 'P1 - Critical', description: 'Production blocking' },
|
||||
{ label: 'P2 - High', description: 'Major functionality' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const priority = parseInt(parseAnswer(prioAnswer).charAt(1));
|
||||
|
||||
// Generate ID and create
|
||||
const id = `ISS-${Date.now()}`;
|
||||
Bash(`ccw issue init ${id} --title "${title}" --priority ${priority}`);
|
||||
|
||||
console.log(`✓ Created issue ${id}`);
|
||||
await viewIssueInteractive(id);
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```javascript
|
||||
function parseAnswer(answer) {
|
||||
// Extract selected option from AskUserQuestion response
|
||||
if (typeof answer === 'string') return answer;
|
||||
if (answer.answers) {
|
||||
const values = Object.values(answer.answers);
|
||||
return values[0] || '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function parseMultiAnswer(answer) {
|
||||
// Extract multiple selections
|
||||
if (typeof answer === 'string') return answer.split(',').map(s => s.trim());
|
||||
if (answer.answers) {
|
||||
const values = Object.values(answer.answers);
|
||||
return values.flatMap(v => v.split(',').map(s => s.trim()));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function parseFlags(input) {
|
||||
const flags = {};
|
||||
const matches = input.matchAll(/--(\w+)\s+([^\s-]+)/g);
|
||||
for (const match of matches) {
|
||||
flags[match[1]] = match[2];
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
function parseIssueId(input) {
|
||||
const match = input.match(/^([A-Z]+-\d+|ISS-\d+|GH-\d+)/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Resolution |
|
||||
|-------|------------|
|
||||
| No issues found | Suggest creating with /issue:new |
|
||||
| Issue not found | Show available issues, ask for correction |
|
||||
| Invalid selection | Show error, re-prompt |
|
||||
| Write failure | Check permissions, show error |
|
||||
| Queue operation fails | Show ccw issue error, suggest fix |
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/issue:new` - Create structured issue
|
||||
- `/issue:plan` - Plan solution for issue
|
||||
- `/issue:queue` - Form execution queue
|
||||
- `/issue:execute` - Execute queued tasks
|
||||
- `ccw issue list` - CLI list command
|
||||
- `ccw issue status` - CLI status command
|
||||
484
.claude/commands/issue/new.md
Normal file
484
.claude/commands/issue/new.md
Normal file
@@ -0,0 +1,484 @@
|
||||
---
|
||||
name: new
|
||||
description: Create structured issue from GitHub URL or text description, extracting key elements into issues.jsonl
|
||||
argument-hint: "<github-url | text-description> [--priority 1-5] [--labels label1,label2]"
|
||||
allowed-tools: TodoWrite(*), Bash(*), Read(*), Write(*), WebFetch(*), AskUserQuestion(*)
|
||||
---
|
||||
|
||||
# Issue New Command (/issue:new)
|
||||
|
||||
## Overview
|
||||
|
||||
Creates a new structured issue from either:
|
||||
1. **GitHub Issue URL** - Fetches and parses issue content via `gh` CLI
|
||||
2. **Text Description** - Parses natural language into structured fields
|
||||
|
||||
Outputs a well-formed issue entry to `.workflow/issues/issues.jsonl`.
|
||||
|
||||
## Issue Structure (Closed-Loop)
|
||||
|
||||
```typescript
|
||||
interface Issue {
|
||||
id: string; // GH-123 or ISS-YYYYMMDD-HHMMSS
|
||||
title: string; // Issue title (clear, concise)
|
||||
status: 'registered'; // Initial status
|
||||
priority: number; // 1 (critical) to 5 (low)
|
||||
context: string; // Problem description
|
||||
source: 'github' | 'text'; // Input source type
|
||||
source_url?: string; // GitHub URL if applicable
|
||||
labels?: string[]; // Categorization labels
|
||||
|
||||
// Structured extraction
|
||||
problem_statement: string; // What is the problem?
|
||||
expected_behavior?: string; // What should happen?
|
||||
actual_behavior?: string; // What actually happens?
|
||||
affected_components?: string[];// Files/modules affected
|
||||
reproduction_steps?: string[]; // Steps to reproduce
|
||||
|
||||
// Closed-loop requirements (guide plan generation)
|
||||
lifecycle_requirements: {
|
||||
test_strategy: 'unit' | 'integration' | 'e2e' | 'manual' | 'auto';
|
||||
regression_scope: 'affected' | 'related' | 'full'; // Which tests to run
|
||||
acceptance_type: 'automated' | 'manual' | 'both'; // How to verify
|
||||
commit_strategy: 'per-task' | 'squash' | 'atomic'; // Commit granularity
|
||||
};
|
||||
|
||||
// Metadata
|
||||
bound_solution_id: null;
|
||||
solution_count: 0;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Task Lifecycle (Each Task is Closed-Loop)
|
||||
|
||||
When `/issue:plan` generates tasks, each task MUST include:
|
||||
|
||||
```typescript
|
||||
interface SolutionTask {
|
||||
id: string;
|
||||
title: string;
|
||||
scope: string;
|
||||
action: string;
|
||||
|
||||
// Phase 1: Implementation
|
||||
implementation: string[]; // Step-by-step implementation
|
||||
modification_points: { file: string; target: string; change: string }[];
|
||||
|
||||
// Phase 2: Testing
|
||||
test: {
|
||||
unit?: string[]; // Unit test requirements
|
||||
integration?: string[]; // Integration test requirements
|
||||
commands?: string[]; // Test commands to run
|
||||
coverage_target?: number; // Minimum coverage %
|
||||
};
|
||||
|
||||
// Phase 3: Regression
|
||||
regression: string[]; // Regression check commands/points
|
||||
|
||||
// Phase 4: Acceptance
|
||||
acceptance: {
|
||||
criteria: string[]; // Testable acceptance criteria
|
||||
verification: string[]; // How to verify each criterion
|
||||
manual_checks?: string[]; // Manual verification if needed
|
||||
};
|
||||
|
||||
// Phase 5: Commit
|
||||
commit: {
|
||||
type: 'feat' | 'fix' | 'refactor' | 'test' | 'docs' | 'chore';
|
||||
scope: string; // e.g., "auth", "api"
|
||||
message_template: string; // Commit message template
|
||||
breaking?: boolean;
|
||||
};
|
||||
|
||||
depends_on: string[];
|
||||
executor: 'codex' | 'gemini' | 'agent' | 'auto';
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# From GitHub URL
|
||||
/issue:new https://github.com/owner/repo/issues/123
|
||||
|
||||
# From text description
|
||||
/issue:new "Login fails when password contains special characters. Expected: successful login. Actual: 500 error. Affects src/auth/*"
|
||||
|
||||
# With options
|
||||
/issue:new <url-or-text> --priority 2 --labels "bug,auth"
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Input Detection
|
||||
|
||||
```javascript
|
||||
const input = userInput.trim();
|
||||
const flags = parseFlags(userInput); // --priority, --labels
|
||||
|
||||
// Detect input type
|
||||
const isGitHubUrl = input.match(/github\.com\/[\w-]+\/[\w-]+\/issues\/\d+/);
|
||||
const isGitHubShort = input.match(/^#(\d+)$/); // #123 format
|
||||
|
||||
let issueData = {};
|
||||
|
||||
if (isGitHubUrl || isGitHubShort) {
|
||||
// GitHub issue - fetch via gh CLI
|
||||
issueData = await fetchGitHubIssue(input);
|
||||
} else {
|
||||
// Text description - parse structure
|
||||
issueData = await parseTextDescription(input);
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: GitHub Issue Fetching
|
||||
|
||||
```javascript
|
||||
async function fetchGitHubIssue(urlOrNumber) {
|
||||
let issueRef;
|
||||
|
||||
if (urlOrNumber.startsWith('http')) {
|
||||
// Extract owner/repo/number from URL
|
||||
const match = urlOrNumber.match(/github\.com\/([\w-]+)\/([\w-]+)\/issues\/(\d+)/);
|
||||
if (!match) throw new Error('Invalid GitHub URL');
|
||||
issueRef = `${match[1]}/${match[2]}#${match[3]}`;
|
||||
} else {
|
||||
// #123 format - use current repo
|
||||
issueRef = urlOrNumber.replace('#', '');
|
||||
}
|
||||
|
||||
// Fetch via gh CLI
|
||||
const result = Bash(`gh issue view ${issueRef} --json number,title,body,labels,state,url`);
|
||||
const ghIssue = JSON.parse(result);
|
||||
|
||||
// Parse body for structure
|
||||
const parsed = parseIssueBody(ghIssue.body);
|
||||
|
||||
return {
|
||||
id: `GH-${ghIssue.number}`,
|
||||
title: ghIssue.title,
|
||||
source: 'github',
|
||||
source_url: ghIssue.url,
|
||||
labels: ghIssue.labels.map(l => l.name),
|
||||
context: ghIssue.body,
|
||||
...parsed
|
||||
};
|
||||
}
|
||||
|
||||
function parseIssueBody(body) {
|
||||
// Extract structured sections from markdown body
|
||||
const sections = {};
|
||||
|
||||
// Problem/Description
|
||||
const problemMatch = body.match(/##?\s*(problem|description|issue)[:\s]*([\s\S]*?)(?=##|$)/i);
|
||||
if (problemMatch) sections.problem_statement = problemMatch[2].trim();
|
||||
|
||||
// Expected behavior
|
||||
const expectedMatch = body.match(/##?\s*(expected|should)[:\s]*([\s\S]*?)(?=##|$)/i);
|
||||
if (expectedMatch) sections.expected_behavior = expectedMatch[2].trim();
|
||||
|
||||
// Actual behavior
|
||||
const actualMatch = body.match(/##?\s*(actual|current)[:\s]*([\s\S]*?)(?=##|$)/i);
|
||||
if (actualMatch) sections.actual_behavior = actualMatch[2].trim();
|
||||
|
||||
// Steps to reproduce
|
||||
const stepsMatch = body.match(/##?\s*(steps|reproduce)[:\s]*([\s\S]*?)(?=##|$)/i);
|
||||
if (stepsMatch) {
|
||||
const stepsText = stepsMatch[2].trim();
|
||||
sections.reproduction_steps = stepsText
|
||||
.split('\n')
|
||||
.filter(line => line.match(/^\s*[\d\-\*]/))
|
||||
.map(line => line.replace(/^\s*[\d\.\-\*]\s*/, '').trim());
|
||||
}
|
||||
|
||||
// Affected components (from file references)
|
||||
const fileMatches = body.match(/`[^`]*\.(ts|js|tsx|jsx|py|go|rs)[^`]*`/g);
|
||||
if (fileMatches) {
|
||||
sections.affected_components = [...new Set(fileMatches.map(f => f.replace(/`/g, '')))];
|
||||
}
|
||||
|
||||
// Fallback: use entire body as problem statement
|
||||
if (!sections.problem_statement) {
|
||||
sections.problem_statement = body.substring(0, 500);
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Text Description Parsing
|
||||
|
||||
```javascript
|
||||
async function parseTextDescription(text) {
|
||||
// Generate unique ID
|
||||
const id = `ISS-${new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14)}`;
|
||||
|
||||
// Extract structured elements using patterns
|
||||
const result = {
|
||||
id,
|
||||
source: 'text',
|
||||
title: '',
|
||||
problem_statement: '',
|
||||
expected_behavior: null,
|
||||
actual_behavior: null,
|
||||
affected_components: [],
|
||||
reproduction_steps: []
|
||||
};
|
||||
|
||||
// Pattern: "Title. Description. Expected: X. Actual: Y. Affects: files"
|
||||
const sentences = text.split(/\.(?=\s|$)/);
|
||||
|
||||
// First sentence as title
|
||||
result.title = sentences[0]?.trim() || 'Untitled Issue';
|
||||
|
||||
// Look for keywords
|
||||
for (const sentence of sentences) {
|
||||
const s = sentence.trim();
|
||||
|
||||
if (s.match(/^expected:?\s*/i)) {
|
||||
result.expected_behavior = s.replace(/^expected:?\s*/i, '');
|
||||
} else if (s.match(/^actual:?\s*/i)) {
|
||||
result.actual_behavior = s.replace(/^actual:?\s*/i, '');
|
||||
} else if (s.match(/^affects?:?\s*/i)) {
|
||||
const components = s.replace(/^affects?:?\s*/i, '').split(/[,\s]+/);
|
||||
result.affected_components = components.filter(c => c.includes('/') || c.includes('.'));
|
||||
} else if (s.match(/^steps?:?\s*/i)) {
|
||||
result.reproduction_steps = s.replace(/^steps?:?\s*/i, '').split(/[,;]/);
|
||||
} else if (!result.problem_statement && s.length > 10) {
|
||||
result.problem_statement = s;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback problem statement
|
||||
if (!result.problem_statement) {
|
||||
result.problem_statement = text.substring(0, 300);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Lifecycle Configuration
|
||||
|
||||
```javascript
|
||||
// Ask for lifecycle requirements (or use smart defaults)
|
||||
const lifecycleAnswer = AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: 'Test strategy for this issue?',
|
||||
header: 'Test',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'auto', description: 'Auto-detect based on affected files (Recommended)' },
|
||||
{ label: 'unit', description: 'Unit tests only' },
|
||||
{ label: 'integration', description: 'Integration tests' },
|
||||
{ label: 'e2e', description: 'End-to-end tests' },
|
||||
{ label: 'manual', description: 'Manual testing only' }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: 'Regression scope?',
|
||||
header: 'Regression',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'affected', description: 'Only affected module tests (Recommended)' },
|
||||
{ label: 'related', description: 'Affected + dependent modules' },
|
||||
{ label: 'full', description: 'Full test suite' }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: 'Commit strategy?',
|
||||
header: 'Commit',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'per-task', description: 'One commit per task (Recommended)' },
|
||||
{ label: 'atomic', description: 'Single commit for entire issue' },
|
||||
{ label: 'squash', description: 'Squash at the end' }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const lifecycle = {
|
||||
test_strategy: lifecycleAnswer.test || 'auto',
|
||||
regression_scope: lifecycleAnswer.regression || 'affected',
|
||||
acceptance_type: 'automated',
|
||||
commit_strategy: lifecycleAnswer.commit || 'per-task'
|
||||
};
|
||||
|
||||
issueData.lifecycle_requirements = lifecycle;
|
||||
```
|
||||
|
||||
### Phase 5: User Confirmation
|
||||
|
||||
```javascript
|
||||
// Show parsed data and ask for confirmation
|
||||
console.log(`
|
||||
## Parsed Issue
|
||||
|
||||
**ID**: ${issueData.id}
|
||||
**Title**: ${issueData.title}
|
||||
**Source**: ${issueData.source}${issueData.source_url ? ` (${issueData.source_url})` : ''}
|
||||
|
||||
### Problem Statement
|
||||
${issueData.problem_statement}
|
||||
|
||||
${issueData.expected_behavior ? `### Expected Behavior\n${issueData.expected_behavior}\n` : ''}
|
||||
${issueData.actual_behavior ? `### Actual Behavior\n${issueData.actual_behavior}\n` : ''}
|
||||
${issueData.affected_components?.length ? `### Affected Components\n${issueData.affected_components.map(c => `- ${c}`).join('\n')}\n` : ''}
|
||||
${issueData.reproduction_steps?.length ? `### Reproduction Steps\n${issueData.reproduction_steps.map((s, i) => `${i+1}. ${s}`).join('\n')}\n` : ''}
|
||||
|
||||
### Lifecycle Configuration
|
||||
- **Test Strategy**: ${lifecycle.test_strategy}
|
||||
- **Regression Scope**: ${lifecycle.regression_scope}
|
||||
- **Commit Strategy**: ${lifecycle.commit_strategy}
|
||||
`);
|
||||
|
||||
// Ask user to confirm or edit
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Create this issue?',
|
||||
header: 'Confirm',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Create', description: 'Save issue to issues.jsonl' },
|
||||
{ label: 'Edit Title', description: 'Modify the issue title' },
|
||||
{ label: 'Edit Priority', description: 'Change priority (1-5)' },
|
||||
{ label: 'Cancel', description: 'Discard and exit' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (answer.includes('Cancel')) {
|
||||
console.log('Issue creation cancelled.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (answer.includes('Edit Title')) {
|
||||
const titleAnswer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Enter new title:',
|
||||
header: 'Title',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: issueData.title.substring(0, 40), description: 'Keep current' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
// Handle custom input via "Other"
|
||||
if (titleAnswer.customText) {
|
||||
issueData.title = titleAnswer.customText;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 6: Write to JSONL
|
||||
|
||||
```javascript
|
||||
// Construct final issue object
|
||||
const priority = flags.priority ? parseInt(flags.priority) : 3;
|
||||
const labels = flags.labels ? flags.labels.split(',').map(l => l.trim()) : [];
|
||||
|
||||
const newIssue = {
|
||||
id: issueData.id,
|
||||
title: issueData.title,
|
||||
status: 'registered',
|
||||
priority,
|
||||
context: issueData.problem_statement,
|
||||
source: issueData.source,
|
||||
source_url: issueData.source_url || null,
|
||||
labels: [...(issueData.labels || []), ...labels],
|
||||
|
||||
// Structured fields
|
||||
problem_statement: issueData.problem_statement,
|
||||
expected_behavior: issueData.expected_behavior || null,
|
||||
actual_behavior: issueData.actual_behavior || null,
|
||||
affected_components: issueData.affected_components || [],
|
||||
reproduction_steps: issueData.reproduction_steps || [],
|
||||
|
||||
// Closed-loop lifecycle requirements
|
||||
lifecycle_requirements: issueData.lifecycle_requirements || {
|
||||
test_strategy: 'auto',
|
||||
regression_scope: 'affected',
|
||||
acceptance_type: 'automated',
|
||||
commit_strategy: 'per-task'
|
||||
},
|
||||
|
||||
// Metadata
|
||||
bound_solution_id: null,
|
||||
solution_count: 0,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Ensure directory exists
|
||||
Bash('mkdir -p .workflow/issues');
|
||||
|
||||
// Append to issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
Bash(`echo '${JSON.stringify(newIssue)}' >> "${issuesPath}"`);
|
||||
|
||||
console.log(`
|
||||
## Issue Created
|
||||
|
||||
**ID**: ${newIssue.id}
|
||||
**Title**: ${newIssue.title}
|
||||
**Priority**: ${newIssue.priority}
|
||||
**Labels**: ${newIssue.labels.join(', ') || 'none'}
|
||||
**Source**: ${newIssue.source}
|
||||
|
||||
### Next Steps
|
||||
1. Plan solution: \`/issue:plan ${newIssue.id}\`
|
||||
2. View details: \`ccw issue status ${newIssue.id}\`
|
||||
3. Manage issues: \`/issue:manage\`
|
||||
`);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### GitHub Issue
|
||||
|
||||
```bash
|
||||
/issue:new https://github.com/myorg/myrepo/issues/42 --priority 2
|
||||
|
||||
# Output:
|
||||
## Issue Created
|
||||
**ID**: GH-42
|
||||
**Title**: Fix memory leak in WebSocket handler
|
||||
**Priority**: 2
|
||||
**Labels**: bug, performance
|
||||
**Source**: github (https://github.com/myorg/myrepo/issues/42)
|
||||
```
|
||||
|
||||
### Text Description
|
||||
|
||||
```bash
|
||||
/issue:new "API rate limiting not working. Expected: 429 after 100 requests. Actual: No limit. Affects src/middleware/rate-limit.ts"
|
||||
|
||||
# Output:
|
||||
## Issue Created
|
||||
**ID**: ISS-20251227-142530
|
||||
**Title**: API rate limiting not working
|
||||
**Priority**: 3
|
||||
**Labels**: none
|
||||
**Source**: text
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Resolution |
|
||||
|-------|------------|
|
||||
| Invalid GitHub URL | Show format hint, ask for correction |
|
||||
| gh CLI not available | Fall back to WebFetch for public issues |
|
||||
| Empty description | Prompt user for required fields |
|
||||
| Duplicate issue ID | Auto-increment or suggest merge |
|
||||
| Parse failure | Show raw input, ask for manual structuring |
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/issue:plan` - Plan solution for issue
|
||||
- `/issue:manage` - Interactive issue management
|
||||
- `ccw issue list` - List all issues
|
||||
- `ccw issue status <id>` - View issue details
|
||||
421
.claude/commands/issue/plan.md
Normal file
421
.claude/commands/issue/plan.md
Normal file
@@ -0,0 +1,421 @@
|
||||
---
|
||||
name: plan
|
||||
description: Batch plan issue resolution using issue-plan-agent (explore + plan closed-loop)
|
||||
argument-hint: "<issue-id>[,<issue-id>,...] [--batch-size 3]"
|
||||
allowed-tools: TodoWrite(*), Task(*), SlashCommand(*), AskUserQuestion(*), Bash(*), Read(*), Write(*)
|
||||
---
|
||||
|
||||
# Issue Plan Command (/issue:plan)
|
||||
|
||||
## Overview
|
||||
|
||||
Unified planning command using **issue-plan-agent** that combines exploration and planning into a single closed-loop workflow. The agent handles ACE semantic search, solution generation, and task breakdown.
|
||||
|
||||
**Core capabilities:**
|
||||
- **Closed-loop agent**: issue-plan-agent combines explore + plan
|
||||
- Batch processing: 1 agent processes 1-3 issues
|
||||
- ACE semantic search integrated into planning
|
||||
- Solution with executable tasks and acceptance criteria
|
||||
- Automatic solution registration and binding
|
||||
|
||||
## Storage Structure (Flat JSONL)
|
||||
|
||||
```
|
||||
.workflow/issues/
|
||||
├── issues.jsonl # All issues (one per line)
|
||||
├── queue.json # Execution queue
|
||||
└── solutions/
|
||||
├── {issue-id}.jsonl # Solutions for issue (one per line)
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/issue:plan <issue-id>[,<issue-id>,...] [FLAGS]
|
||||
|
||||
# Examples
|
||||
/issue:plan GH-123 # Single issue
|
||||
/issue:plan GH-123,GH-124,GH-125 # Batch (up to 3)
|
||||
/issue:plan --all-pending # All pending issues
|
||||
|
||||
# Flags
|
||||
--batch-size <n> Max issues per agent batch (default: 3)
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Issue Loading
|
||||
├─ Parse input (single, comma-separated, or --all-pending)
|
||||
├─ Load issues from .workflow/issues/issues.jsonl
|
||||
├─ Validate issues exist (create if needed)
|
||||
└─ Group into batches (max 3 per batch)
|
||||
|
||||
Phase 2: Unified Explore + Plan (issue-plan-agent)
|
||||
├─ Launch issue-plan-agent per batch
|
||||
├─ Agent performs:
|
||||
│ ├─ ACE semantic search for each issue
|
||||
│ ├─ Codebase exploration (files, patterns, dependencies)
|
||||
│ ├─ Solution generation with task breakdown
|
||||
│ └─ Conflict detection across issues
|
||||
└─ Output: solution JSON per issue
|
||||
|
||||
Phase 3: Solution Registration & Binding
|
||||
├─ Append solutions to solutions/{issue-id}.jsonl
|
||||
├─ Single solution per issue → auto-bind
|
||||
├─ Multiple candidates → AskUserQuestion to select
|
||||
└─ Update issues.jsonl with bound_solution_id
|
||||
|
||||
Phase 4: Summary
|
||||
├─ Display bound solutions
|
||||
├─ Show task counts per issue
|
||||
└─ Display next steps (/issue:queue)
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Issue Loading
|
||||
|
||||
```javascript
|
||||
// Parse input
|
||||
const issueIds = userInput.includes(',')
|
||||
? userInput.split(',').map(s => s.trim())
|
||||
: [userInput.trim()];
|
||||
|
||||
// Read issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
const allIssues = Bash(`cat "${issuesPath}" 2>/dev/null || echo ''`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
// Load and validate issues
|
||||
const issues = [];
|
||||
for (const id of issueIds) {
|
||||
let issue = allIssues.find(i => i.id === id);
|
||||
|
||||
if (!issue) {
|
||||
console.log(`Issue ${id} not found. Creating...`);
|
||||
issue = {
|
||||
id,
|
||||
title: `Issue ${id}`,
|
||||
status: 'registered',
|
||||
priority: 3,
|
||||
context: '',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
// Append to issues.jsonl
|
||||
Bash(`echo '${JSON.stringify(issue)}' >> "${issuesPath}"`);
|
||||
}
|
||||
|
||||
issues.push(issue);
|
||||
}
|
||||
|
||||
// Group into batches
|
||||
const batchSize = flags.batchSize || 3;
|
||||
const batches = [];
|
||||
for (let i = 0; i < issues.length; i += batchSize) {
|
||||
batches.push(issues.slice(i, i + batchSize));
|
||||
}
|
||||
|
||||
TodoWrite({
|
||||
todos: batches.flatMap((batch, i) => [
|
||||
{ content: `Plan batch ${i+1}`, status: 'pending', activeForm: `Planning batch ${i+1}` }
|
||||
])
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 2: Unified Explore + Plan (issue-plan-agent)
|
||||
|
||||
```javascript
|
||||
for (const [batchIndex, batch] of batches.entries()) {
|
||||
updateTodo(`Plan batch ${batchIndex + 1}`, 'in_progress');
|
||||
|
||||
// Build issue prompt for agent with lifecycle requirements
|
||||
const issuePrompt = `
|
||||
## Issues to Plan (Closed-Loop Tasks Required)
|
||||
|
||||
${batch.map((issue, i) => `
|
||||
### Issue ${i + 1}: ${issue.id}
|
||||
**Title**: ${issue.title}
|
||||
**Context**: ${issue.context || 'No context provided'}
|
||||
**Affected Components**: ${issue.affected_components?.join(', ') || 'Not specified'}
|
||||
|
||||
**Lifecycle Requirements**:
|
||||
- Test Strategy: ${issue.lifecycle_requirements?.test_strategy || 'auto'}
|
||||
- Regression Scope: ${issue.lifecycle_requirements?.regression_scope || 'affected'}
|
||||
- Commit Strategy: ${issue.lifecycle_requirements?.commit_strategy || 'per-task'}
|
||||
`).join('\n')}
|
||||
|
||||
## Project Root
|
||||
${process.cwd()}
|
||||
|
||||
## Requirements - CLOSED-LOOP TASKS
|
||||
|
||||
Each task MUST include ALL lifecycle phases:
|
||||
|
||||
### 1. Implementation
|
||||
- implementation: string[] (2-7 concrete steps)
|
||||
- modification_points: { file, target, change }[]
|
||||
|
||||
### 2. Test
|
||||
- test.unit: string[] (unit test requirements)
|
||||
- test.integration: string[] (integration test requirements if needed)
|
||||
- test.commands: string[] (actual test commands to run)
|
||||
- test.coverage_target: number (minimum coverage %)
|
||||
|
||||
### 3. Regression
|
||||
- regression: string[] (commands to run for regression check)
|
||||
- Based on issue's regression_scope setting
|
||||
|
||||
### 4. Acceptance
|
||||
- acceptance.criteria: string[] (testable acceptance criteria)
|
||||
- acceptance.verification: string[] (how to verify each criterion)
|
||||
- acceptance.manual_checks: string[] (manual checks if needed)
|
||||
|
||||
### 5. Commit
|
||||
- commit.type: feat|fix|refactor|test|docs|chore
|
||||
- commit.scope: string (module name)
|
||||
- commit.message_template: string (full commit message)
|
||||
- commit.breaking: boolean
|
||||
|
||||
## Additional Requirements
|
||||
1. Use ACE semantic search (mcp__ace-tool__search_context) for exploration
|
||||
2. Detect file conflicts if multiple issues
|
||||
3. Generate executable test commands based on project's test framework
|
||||
4. Infer commit scope from affected files
|
||||
`;
|
||||
|
||||
// Launch issue-plan-agent (combines explore + plan)
|
||||
const result = Task(
|
||||
subagent_type="issue-plan-agent",
|
||||
run_in_background=false,
|
||||
description=`Explore & plan ${batch.length} issues`,
|
||||
prompt=issuePrompt
|
||||
);
|
||||
|
||||
// Parse agent output
|
||||
const agentOutput = JSON.parse(result);
|
||||
|
||||
// Register solutions for each issue (append to solutions/{issue-id}.jsonl)
|
||||
for (const item of agentOutput.solutions) {
|
||||
const solutionPath = `.workflow/issues/solutions/${item.issue_id}.jsonl`;
|
||||
|
||||
// Ensure solutions directory exists
|
||||
Bash(`mkdir -p .workflow/issues/solutions`);
|
||||
|
||||
// Append solution as new line
|
||||
Bash(`echo '${JSON.stringify(item.solution)}' >> "${solutionPath}"`);
|
||||
}
|
||||
|
||||
// Handle conflicts if any
|
||||
if (agentOutput.conflicts?.length > 0) {
|
||||
console.log(`\n⚠ File conflicts detected:`);
|
||||
agentOutput.conflicts.forEach(c => {
|
||||
console.log(` ${c.file}: ${c.issues.join(', ')} → suggested: ${c.suggested_order.join(' → ')}`);
|
||||
});
|
||||
}
|
||||
|
||||
updateTodo(`Plan batch ${batchIndex + 1}`, 'completed');
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Solution Binding
|
||||
|
||||
```javascript
|
||||
// Re-read issues.jsonl
|
||||
let allIssuesUpdated = Bash(`cat "${issuesPath}"`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
for (const issue of issues) {
|
||||
const solPath = `.workflow/issues/solutions/${issue.id}.jsonl`;
|
||||
const solutions = Bash(`cat "${solPath}" 2>/dev/null || echo ''`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
if (solutions.length === 0) {
|
||||
console.log(`⚠ No solutions for ${issue.id}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let selectedSolId;
|
||||
|
||||
if (solutions.length === 1) {
|
||||
// Auto-bind single solution
|
||||
selectedSolId = solutions[0].id;
|
||||
console.log(`✓ Auto-bound ${selectedSolId} to ${issue.id} (${solutions[0].tasks?.length || 0} tasks)`);
|
||||
} else {
|
||||
// Multiple solutions - ask user
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Select solution for ${issue.id}:`,
|
||||
header: issue.id,
|
||||
multiSelect: false,
|
||||
options: solutions.map(s => ({
|
||||
label: `${s.id}: ${s.description || 'Solution'}`,
|
||||
description: `${s.tasks?.length || 0} tasks`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
|
||||
selectedSolId = extractSelectedSolutionId(answer);
|
||||
console.log(`✓ Bound ${selectedSolId} to ${issue.id}`);
|
||||
}
|
||||
|
||||
// Update issue in allIssuesUpdated
|
||||
const issueIndex = allIssuesUpdated.findIndex(i => i.id === issue.id);
|
||||
if (issueIndex !== -1) {
|
||||
allIssuesUpdated[issueIndex].bound_solution_id = selectedSolId;
|
||||
allIssuesUpdated[issueIndex].status = 'planned';
|
||||
allIssuesUpdated[issueIndex].planned_at = new Date().toISOString();
|
||||
allIssuesUpdated[issueIndex].updated_at = new Date().toISOString();
|
||||
}
|
||||
|
||||
// Mark solution as bound in solutions file
|
||||
const updatedSolutions = solutions.map(s => ({
|
||||
...s,
|
||||
is_bound: s.id === selectedSolId,
|
||||
bound_at: s.id === selectedSolId ? new Date().toISOString() : s.bound_at
|
||||
}));
|
||||
Write(solPath, updatedSolutions.map(s => JSON.stringify(s)).join('\n'));
|
||||
}
|
||||
|
||||
// Write updated issues.jsonl
|
||||
Write(issuesPath, allIssuesUpdated.map(i => JSON.stringify(i)).join('\n'));
|
||||
```
|
||||
|
||||
### Phase 4: Summary
|
||||
|
||||
```javascript
|
||||
console.log(`
|
||||
## Planning Complete
|
||||
|
||||
**Issues Planned**: ${issues.length}
|
||||
|
||||
### Bound Solutions
|
||||
${issues.map(i => {
|
||||
const issue = allIssuesUpdated.find(a => a.id === i.id);
|
||||
return issue?.bound_solution_id
|
||||
? `✓ ${i.id}: ${issue.bound_solution_id}`
|
||||
: `○ ${i.id}: No solution bound`;
|
||||
}).join('\n')}
|
||||
|
||||
### Next Steps
|
||||
1. Review: \`ccw issue status <issue-id>\`
|
||||
2. Form queue: \`/issue:queue\`
|
||||
3. Execute: \`/issue:execute\`
|
||||
`);
|
||||
```
|
||||
|
||||
## Solution Format (Closed-Loop Tasks)
|
||||
|
||||
Each solution line in `solutions/{issue-id}.jsonl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "SOL-20251226-001",
|
||||
"description": "Direct Implementation",
|
||||
"tasks": [
|
||||
{
|
||||
"id": "T1",
|
||||
"title": "Create auth middleware",
|
||||
"scope": "src/middleware/",
|
||||
"action": "Create",
|
||||
"description": "Create JWT validation middleware",
|
||||
"modification_points": [
|
||||
{ "file": "src/middleware/auth.ts", "target": "new file", "change": "Create middleware" }
|
||||
],
|
||||
|
||||
"implementation": [
|
||||
"Create auth.ts file in src/middleware/",
|
||||
"Implement JWT token validation using jsonwebtoken",
|
||||
"Add error handling for invalid/expired tokens",
|
||||
"Export middleware function"
|
||||
],
|
||||
|
||||
"test": {
|
||||
"unit": [
|
||||
"Test valid token passes through",
|
||||
"Test invalid token returns 401",
|
||||
"Test expired token returns 401",
|
||||
"Test missing token returns 401"
|
||||
],
|
||||
"commands": [
|
||||
"npm test -- --grep 'auth middleware'",
|
||||
"npm run test:coverage -- src/middleware/auth.ts"
|
||||
],
|
||||
"coverage_target": 80
|
||||
},
|
||||
|
||||
"regression": [
|
||||
"npm test -- --grep 'protected routes'",
|
||||
"npm run test:integration -- auth"
|
||||
],
|
||||
|
||||
"acceptance": {
|
||||
"criteria": [
|
||||
"Middleware validates JWT tokens successfully",
|
||||
"Returns 401 for invalid or missing tokens",
|
||||
"Passes decoded token to request context"
|
||||
],
|
||||
"verification": [
|
||||
"curl -H 'Authorization: Bearer valid_token' /api/protected → 200",
|
||||
"curl /api/protected → 401",
|
||||
"curl -H 'Authorization: Bearer invalid' /api/protected → 401"
|
||||
]
|
||||
},
|
||||
|
||||
"commit": {
|
||||
"type": "feat",
|
||||
"scope": "auth",
|
||||
"message_template": "feat(auth): add JWT validation middleware\n\n- Implement token validation\n- Add error handling for invalid tokens\n- Export for route protection",
|
||||
"breaking": false
|
||||
},
|
||||
|
||||
"depends_on": [],
|
||||
"estimated_minutes": 30,
|
||||
"executor": "codex"
|
||||
}
|
||||
],
|
||||
"exploration_context": {
|
||||
"relevant_files": ["src/config/auth.ts"],
|
||||
"patterns": "Follow existing middleware pattern"
|
||||
},
|
||||
"is_bound": true,
|
||||
"created_at": "2025-12-26T10:00:00Z",
|
||||
"bound_at": "2025-12-26T10:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Resolution |
|
||||
|-------|------------|
|
||||
| Issue not found | Auto-create in issues.jsonl |
|
||||
| ACE search fails | Agent falls back to ripgrep |
|
||||
| No solutions generated | Display error, suggest manual planning |
|
||||
| User cancels selection | Skip issue, continue with others |
|
||||
| File conflicts | Agent detects and suggests resolution order |
|
||||
|
||||
## Agent Integration
|
||||
|
||||
The command uses `issue-plan-agent` which:
|
||||
1. Performs ACE semantic search per issue
|
||||
2. Identifies modification points and patterns
|
||||
3. Generates task breakdown with dependencies
|
||||
4. Detects cross-issue file conflicts
|
||||
5. Outputs solution JSON for registration
|
||||
|
||||
See `.claude/agents/issue-plan-agent.md` for agent specification.
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/issue:queue` - Form execution queue from bound solutions
|
||||
- `/issue:execute` - Execute queue with codex
|
||||
- `ccw issue list` - List all issues
|
||||
- `ccw issue status` - View issue and solution details
|
||||
354
.claude/commands/issue/queue.md
Normal file
354
.claude/commands/issue/queue.md
Normal file
@@ -0,0 +1,354 @@
|
||||
---
|
||||
name: queue
|
||||
description: Form execution queue from bound solutions using issue-queue-agent
|
||||
argument-hint: "[--rebuild] [--issue <id>]"
|
||||
allowed-tools: TodoWrite(*), Task(*), Bash(*), Read(*), Write(*)
|
||||
---
|
||||
|
||||
# Issue Queue Command (/issue:queue)
|
||||
|
||||
## Overview
|
||||
|
||||
Queue formation command using **issue-queue-agent** that analyzes all bound solutions, resolves conflicts, determines dependencies, and creates an ordered execution queue. The queue is global across all issues.
|
||||
|
||||
**Core capabilities:**
|
||||
- **Agent-driven**: issue-queue-agent handles all ordering logic
|
||||
- ACE semantic search for relationship discovery
|
||||
- Dependency DAG construction and cycle detection
|
||||
- File conflict detection and resolution
|
||||
- Semantic priority calculation (0.0-1.0)
|
||||
- Parallel/Sequential group assignment
|
||||
- Output global queue.json
|
||||
|
||||
## Storage Structure (Queue History)
|
||||
|
||||
```
|
||||
.workflow/issues/
|
||||
├── issues.jsonl # All issues (one per line)
|
||||
├── queues/ # Queue history directory
|
||||
│ ├── index.json # Queue index (active + history)
|
||||
│ ├── {queue-id}.json # Individual queue files
|
||||
│ └── ...
|
||||
└── solutions/
|
||||
├── {issue-id}.jsonl # Solutions for issue
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Queue Index Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"active_queue_id": "QUE-20251227-143000",
|
||||
"queues": [
|
||||
{
|
||||
"id": "QUE-20251227-143000",
|
||||
"status": "active",
|
||||
"issue_ids": ["GH-123", "GH-124"],
|
||||
"total_tasks": 8,
|
||||
"completed_tasks": 3,
|
||||
"created_at": "2025-12-27T14:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": "QUE-20251226-100000",
|
||||
"status": "completed",
|
||||
"issue_ids": ["GH-120"],
|
||||
"total_tasks": 5,
|
||||
"completed_tasks": 5,
|
||||
"created_at": "2025-12-26T10:00:00Z",
|
||||
"completed_at": "2025-12-26T12:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/issue:queue [FLAGS]
|
||||
|
||||
# Examples
|
||||
/issue:queue # Form NEW queue from all bound solutions
|
||||
/issue:queue --issue GH-123 # Form queue for specific issue only
|
||||
/issue:queue --append GH-124 # Append to active queue
|
||||
/issue:queue --list # List all queues (history)
|
||||
/issue:queue --switch QUE-xxx # Switch active queue
|
||||
/issue:queue --archive # Archive completed active queue
|
||||
|
||||
# Flags
|
||||
--issue <id> Form queue for specific issue only
|
||||
--append <id> Append issue to active queue (don't create new)
|
||||
--list List all queues with status
|
||||
--switch <queue-id> Switch active queue
|
||||
--archive Archive current queue (mark completed)
|
||||
--clear <queue-id> Delete a queue from history
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Solution Loading
|
||||
├─ Load issues.jsonl
|
||||
├─ Filter issues with bound_solution_id
|
||||
├─ Read solutions/{issue-id}.jsonl for each issue
|
||||
├─ Find bound solution by ID
|
||||
└─ Extract tasks from bound solutions
|
||||
|
||||
Phase 2-4: Agent-Driven Queue Formation (issue-queue-agent)
|
||||
├─ Launch issue-queue-agent with all tasks
|
||||
├─ Agent performs:
|
||||
│ ├─ Build dependency DAG from depends_on
|
||||
│ ├─ Detect circular dependencies
|
||||
│ ├─ Identify file modification conflicts
|
||||
│ ├─ Resolve conflicts using ordering rules
|
||||
│ ├─ Calculate semantic priority (0.0-1.0)
|
||||
│ └─ Assign execution groups (parallel/sequential)
|
||||
└─ Output: queue JSON with ordered tasks
|
||||
|
||||
Phase 5: Queue Output
|
||||
├─ Write queue.json
|
||||
├─ Update issue statuses in issues.jsonl
|
||||
└─ Display queue summary
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Solution Loading
|
||||
|
||||
```javascript
|
||||
// Load issues.jsonl
|
||||
const issuesPath = '.workflow/issues/issues.jsonl';
|
||||
const allIssues = Bash(`cat "${issuesPath}" 2>/dev/null || echo ''`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
// Filter issues with bound solutions
|
||||
const plannedIssues = allIssues.filter(i =>
|
||||
i.status === 'planned' && i.bound_solution_id
|
||||
);
|
||||
|
||||
if (plannedIssues.length === 0) {
|
||||
console.log('No issues with bound solutions found.');
|
||||
console.log('Run /issue:plan first to create and bind solutions.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all tasks from bound solutions
|
||||
const allTasks = [];
|
||||
for (const issue of plannedIssues) {
|
||||
const solPath = `.workflow/issues/solutions/${issue.id}.jsonl`;
|
||||
const solutions = Bash(`cat "${solPath}" 2>/dev/null || echo ''`)
|
||||
.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
// Find bound solution
|
||||
const boundSol = solutions.find(s => s.id === issue.bound_solution_id);
|
||||
|
||||
if (!boundSol) {
|
||||
console.log(`⚠ Bound solution ${issue.bound_solution_id} not found for ${issue.id}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const task of boundSol.tasks || []) {
|
||||
allTasks.push({
|
||||
issue_id: issue.id,
|
||||
solution_id: issue.bound_solution_id,
|
||||
task,
|
||||
exploration_context: boundSol.exploration_context
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Loaded ${allTasks.length} tasks from ${plannedIssues.length} issues`);
|
||||
```
|
||||
|
||||
### Phase 2-4: Agent-Driven Queue Formation
|
||||
|
||||
```javascript
|
||||
// Launch issue-queue-agent to handle all ordering logic
|
||||
const agentPrompt = `
|
||||
## Tasks to Order
|
||||
|
||||
${JSON.stringify(allTasks, null, 2)}
|
||||
|
||||
## Project Root
|
||||
${process.cwd()}
|
||||
|
||||
## Requirements
|
||||
1. Build dependency DAG from depends_on fields
|
||||
2. Detect circular dependencies (abort if found)
|
||||
3. Identify file modification conflicts
|
||||
4. Resolve conflicts using ordering rules:
|
||||
- Create before Update/Implement
|
||||
- Foundation scopes (config/types) before implementation
|
||||
- Core logic before tests
|
||||
5. Calculate semantic priority (0.0-1.0) for each task
|
||||
6. Assign execution groups (parallel P* / sequential S*)
|
||||
7. Output queue JSON
|
||||
`;
|
||||
|
||||
const result = Task(
|
||||
subagent_type="issue-queue-agent",
|
||||
run_in_background=false,
|
||||
description=`Order ${allTasks.length} tasks from ${plannedIssues.length} issues`,
|
||||
prompt=agentPrompt
|
||||
);
|
||||
|
||||
// Parse agent output
|
||||
const agentOutput = JSON.parse(result);
|
||||
|
||||
if (!agentOutput.success) {
|
||||
console.error(`Queue formation failed: ${agentOutput.error}`);
|
||||
if (agentOutput.cycles) {
|
||||
console.error('Circular dependencies:', agentOutput.cycles.join(', '));
|
||||
}
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Queue Output & Summary
|
||||
|
||||
```javascript
|
||||
const queueOutput = agentOutput.output;
|
||||
|
||||
// Write queue.json
|
||||
Write('.workflow/issues/queue.json', JSON.stringify(queueOutput, null, 2));
|
||||
|
||||
// Update issue statuses in issues.jsonl
|
||||
const updatedIssues = allIssues.map(issue => {
|
||||
if (plannedIssues.find(p => p.id === issue.id)) {
|
||||
return {
|
||||
...issue,
|
||||
status: 'queued',
|
||||
queued_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
return issue;
|
||||
});
|
||||
|
||||
Write(issuesPath, updatedIssues.map(i => JSON.stringify(i)).join('\n'));
|
||||
|
||||
// Display summary
|
||||
console.log(`
|
||||
## Queue Formed
|
||||
|
||||
**Total Tasks**: ${queueOutput.queue.length}
|
||||
**Issues**: ${plannedIssues.length}
|
||||
**Conflicts**: ${queueOutput.conflicts?.length || 0} (${queueOutput._metadata?.resolved_conflicts || 0} resolved)
|
||||
|
||||
### Execution Groups
|
||||
${(queueOutput.execution_groups || []).map(g => {
|
||||
const type = g.type === 'parallel' ? 'Parallel' : 'Sequential';
|
||||
return `- ${g.id} (${type}): ${g.task_count} tasks`;
|
||||
}).join('\n')}
|
||||
|
||||
### Next Steps
|
||||
1. Review queue: \`ccw issue queue list\`
|
||||
2. Execute: \`/issue:execute\`
|
||||
`);
|
||||
```
|
||||
|
||||
## Queue Schema
|
||||
|
||||
Output `queues/{queue-id}.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "QUE-20251227-143000",
|
||||
"name": "Auth Feature Queue",
|
||||
"status": "active",
|
||||
"issue_ids": ["GH-123", "GH-124"],
|
||||
|
||||
"queue": [
|
||||
{
|
||||
"queue_id": "Q-001",
|
||||
"issue_id": "GH-123",
|
||||
"solution_id": "SOL-001",
|
||||
"task_id": "T1",
|
||||
"status": "pending",
|
||||
"execution_order": 1,
|
||||
"execution_group": "P1",
|
||||
"depends_on": [],
|
||||
"semantic_priority": 0.7,
|
||||
"queued_at": "2025-12-26T10:00:00Z"
|
||||
}
|
||||
],
|
||||
|
||||
"conflicts": [
|
||||
{
|
||||
"type": "file_conflict",
|
||||
"file": "src/auth.ts",
|
||||
"tasks": ["GH-123:T1", "GH-124:T2"],
|
||||
"resolution": "sequential",
|
||||
"resolution_order": ["GH-123:T1", "GH-124:T2"],
|
||||
"rationale": "T1 creates file before T2 updates",
|
||||
"resolved": true
|
||||
}
|
||||
],
|
||||
|
||||
"execution_groups": [
|
||||
{ "id": "P1", "type": "parallel", "task_count": 3, "tasks": ["GH-123:T1", "GH-124:T1", "GH-125:T1"] },
|
||||
{ "id": "S2", "type": "sequential", "task_count": 2, "tasks": ["GH-123:T2", "GH-124:T2"] }
|
||||
],
|
||||
|
||||
"_metadata": {
|
||||
"version": "2.0",
|
||||
"total_tasks": 5,
|
||||
"pending_count": 3,
|
||||
"completed_count": 2,
|
||||
"failed_count": 0,
|
||||
"created_at": "2025-12-26T10:00:00Z",
|
||||
"updated_at": "2025-12-26T11:00:00Z",
|
||||
"source": "issue-queue-agent"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Queue ID Format
|
||||
|
||||
```
|
||||
QUE-YYYYMMDD-HHMMSS
|
||||
例如: QUE-20251227-143052
|
||||
```
|
||||
|
||||
## Semantic Priority Rules
|
||||
|
||||
| Factor | Priority Boost |
|
||||
|--------|---------------|
|
||||
| Create action | +0.2 |
|
||||
| Configure action | +0.15 |
|
||||
| Implement action | +0.1 |
|
||||
| Config/Types scope | +0.1 |
|
||||
| Refactor action | -0.05 |
|
||||
| Test action | -0.1 |
|
||||
| Delete action | -0.15 |
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Resolution |
|
||||
|-------|------------|
|
||||
| No bound solutions | Display message, suggest /issue:plan |
|
||||
| Circular dependency | List cycles, abort queue formation |
|
||||
| Unresolved conflicts | Agent resolves using ordering rules |
|
||||
| Invalid task reference | Skip and warn |
|
||||
|
||||
## Agent Integration
|
||||
|
||||
The command uses `issue-queue-agent` which:
|
||||
1. Builds dependency DAG from task depends_on fields
|
||||
2. Detects circular dependencies (aborts if found)
|
||||
3. Identifies file modification conflicts across issues
|
||||
4. Resolves conflicts using semantic ordering rules
|
||||
5. Calculates priority (0.0-1.0) for each task
|
||||
6. Assigns parallel/sequential execution groups
|
||||
7. Outputs structured queue JSON
|
||||
|
||||
See `.claude/agents/issue-queue-agent.md` for agent specification.
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/issue:plan` - Plan issues and bind solutions
|
||||
- `/issue:execute` - Execute queue with codex
|
||||
- `ccw issue queue list` - View current queue
|
||||
@@ -5,7 +5,7 @@ argument-hint: "[--dry-run] [\"focus area\"]"
|
||||
allowed-tools: TodoWrite(*), Task(*), AskUserQuestion(*), Read(*), Glob(*), Bash(*), Write(*)
|
||||
---
|
||||
|
||||
# Clean Command (/clean)
|
||||
# Clean Command (/workflow:clean)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -20,9 +20,9 @@ Intelligent cleanup command that explores the codebase to identify the developme
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/clean # Full intelligent cleanup (explore → analyze → confirm → execute)
|
||||
/clean --dry-run # Explore and analyze only, no execution
|
||||
/clean "auth module" # Focus cleanup on specific area
|
||||
/workflow:clean # Full intelligent cleanup (explore → analyze → confirm → execute)
|
||||
/workflow:clean --dry-run # Explore and analyze only, no execution
|
||||
/workflow:clean "auth module" # Focus cleanup on specific area
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
@@ -321,7 +321,7 @@ if (flags.includes('--dry-run')) {
|
||||
**Dry-run mode**: No changes made.
|
||||
Manifest saved to: ${sessionFolder}/cleanup-manifest.json
|
||||
|
||||
To execute cleanup: /clean
|
||||
To execute cleanup: /workflow:clean
|
||||
`)
|
||||
return
|
||||
}
|
||||
1467
.claude/commands/workflow/docs/analyze.md
Normal file
1467
.claude/commands/workflow/docs/analyze.md
Normal file
File diff suppressed because it is too large
Load Diff
1265
.claude/commands/workflow/docs/copyright.md
Normal file
1265
.claude/commands/workflow/docs/copyright.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -410,7 +410,6 @@ Task(subagent_type="{meta.agent}",
|
||||
1. Read complete task JSON: {session.task_json_path}
|
||||
2. Load context package: {session.context_package_path}
|
||||
|
||||
Follow complete execution guidelines in @.claude/agents/{meta.agent}.md
|
||||
|
||||
**Session Paths**:
|
||||
- Workflow Dir: {session.workflow_dir}
|
||||
|
||||
@@ -15,7 +15,7 @@ Intelligent lightweight planning command with dynamic workflow adaptation based
|
||||
- Intelligent task analysis with automatic exploration detection
|
||||
- Dynamic code exploration (cli-explore-agent) when codebase understanding needed
|
||||
- Interactive clarification after exploration to gather missing information
|
||||
- Adaptive planning strategy (direct Claude vs cli-lite-planning-agent) based on complexity
|
||||
- Adaptive planning: Low complexity → Direct Claude; Medium/High → cli-lite-planning-agent
|
||||
- Two-step confirmation: plan display → multi-dimensional input collection
|
||||
- Execution dispatch with complete context handoff to lite-execute
|
||||
|
||||
@@ -38,7 +38,7 @@ Phase 1: Task Analysis & Exploration
|
||||
├─ Parse input (description or .md file)
|
||||
├─ intelligent complexity assessment (Low/Medium/High)
|
||||
├─ Exploration decision (auto-detect or --explore flag)
|
||||
├─ ⚠️ Context protection: If file reading ≥50k chars → force cli-explore-agent
|
||||
├─ Context protection: If file reading ≥50k chars → force cli-explore-agent
|
||||
└─ Decision:
|
||||
├─ needsExploration=true → Launch parallel cli-explore-agents (1-4 based on complexity)
|
||||
└─ needsExploration=false → Skip to Phase 2/3
|
||||
@@ -140,11 +140,17 @@ function selectAngles(taskDescription, count) {
|
||||
|
||||
const selectedAngles = selectAngles(task_description, complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1))
|
||||
|
||||
// Planning strategy determination
|
||||
const planningStrategy = complexity === 'Low'
|
||||
? 'Direct Claude Planning'
|
||||
: 'cli-lite-planning-agent'
|
||||
|
||||
console.log(`
|
||||
## Exploration Plan
|
||||
|
||||
Task Complexity: ${complexity}
|
||||
Selected Angles: ${selectedAngles.join(', ')}
|
||||
Planning Strategy: ${planningStrategy}
|
||||
|
||||
Launching ${selectedAngles.length} parallel explorations...
|
||||
`)
|
||||
@@ -358,10 +364,7 @@ if (dedupedClarifications.length > 0) {
|
||||
```javascript
|
||||
// 分配规则(优先级从高到低):
|
||||
// 1. 用户明确指定:"用 gemini 分析..." → gemini, "codex 实现..." → codex
|
||||
// 2. 任务类型推断:
|
||||
// - 分析|审查|评估|探索 → gemini
|
||||
// - 实现|创建|修改|修复 → codex (复杂) 或 agent (简单)
|
||||
// 3. 默认 → agent
|
||||
// 2. 默认 → agent
|
||||
|
||||
const executorAssignments = {} // { taskId: { executor: 'gemini'|'codex'|'agent', reason: string } }
|
||||
plan.tasks.forEach(task => {
|
||||
|
||||
@@ -124,6 +124,9 @@ Task(subagent_type="cli-execution-agent", run_in_background=false, prompt=`
|
||||
|
||||
## Analysis Steps
|
||||
|
||||
### 0. Load Output Schema (MANDATORY)
|
||||
Execute: cat ~/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json
|
||||
|
||||
### 1. Load Context
|
||||
- Read existing files from conflict_detection.existing_files
|
||||
- Load plan from .workflow/active/{session_id}/.process/context-package.json
|
||||
@@ -171,123 +174,14 @@ Task(subagent_type="cli-execution-agent", run_in_background=false, prompt=`
|
||||
|
||||
⚠️ Output to conflict-resolution.json (generated in Phase 4)
|
||||
|
||||
Return JSON format for programmatic processing:
|
||||
**Schema Reference**: Execute \`cat ~/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json\` to get full schema
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"conflicts": [
|
||||
{
|
||||
"id": "CON-001",
|
||||
"brief": "一行中文冲突摘要",
|
||||
"severity": "Critical|High|Medium",
|
||||
"category": "Architecture|API|Data|Dependency|ModuleOverlap",
|
||||
"affected_files": [
|
||||
".workflow/active/{session}/.brainstorm/guidance-specification.md",
|
||||
".workflow/active/{session}/.brainstorm/system-architect/analysis.md"
|
||||
],
|
||||
"description": "详细描述冲突 - 什么不兼容",
|
||||
"impact": {
|
||||
"scope": "影响的模块/组件",
|
||||
"compatibility": "Yes|No|Partial",
|
||||
"migration_required": true|false,
|
||||
"estimated_effort": "人天估计"
|
||||
},
|
||||
"overlap_analysis": {
|
||||
"// NOTE": "仅当 category=ModuleOverlap 时需要此字段",
|
||||
"new_module": {
|
||||
"name": "新模块名称",
|
||||
"scenarios": ["场景1", "场景2", "场景3"],
|
||||
"responsibilities": "职责描述"
|
||||
},
|
||||
"existing_modules": [
|
||||
{
|
||||
"file": "src/existing/module.ts",
|
||||
"name": "现有模块名称",
|
||||
"scenarios": ["场景A", "场景B"],
|
||||
"overlap_scenarios": ["重叠场景1", "重叠场景2"],
|
||||
"responsibilities": "现有模块职责"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"name": "策略名称(中文)",
|
||||
"approach": "实现方法简述",
|
||||
"complexity": "Low|Medium|High",
|
||||
"risk": "Low|Medium|High",
|
||||
"effort": "时间估计",
|
||||
"pros": ["优点1", "优点2"],
|
||||
"cons": ["缺点1", "缺点2"],
|
||||
"clarification_needed": [
|
||||
"// NOTE: 仅当需要用户进一步澄清时需要此字段(尤其是 ModuleOverlap)",
|
||||
"新模块的核心职责边界是什么?",
|
||||
"如何与现有模块 X 协作?",
|
||||
"哪些场景应该由新模块处理?"
|
||||
],
|
||||
"modifications": [
|
||||
{
|
||||
"file": ".workflow/active/{session}/.brainstorm/guidance-specification.md",
|
||||
"section": "## 2. System Architect Decisions",
|
||||
"change_type": "update",
|
||||
"old_content": "原始内容片段(用于定位)",
|
||||
"new_content": "修改后的内容",
|
||||
"rationale": "为什么这样改"
|
||||
},
|
||||
{
|
||||
"file": ".workflow/active/{session}/.brainstorm/system-architect/analysis.md",
|
||||
"section": "## Design Decisions",
|
||||
"change_type": "update",
|
||||
"old_content": "原始内容片段",
|
||||
"new_content": "修改后的内容",
|
||||
"rationale": "修改理由"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "策略2名称",
|
||||
"approach": "...",
|
||||
"complexity": "Medium",
|
||||
"risk": "Low",
|
||||
"effort": "1-2天",
|
||||
"pros": ["优点"],
|
||||
"cons": ["缺点"],
|
||||
"modifications": [...]
|
||||
}
|
||||
],
|
||||
"recommended": 0,
|
||||
"modification_suggestions": [
|
||||
"建议1:具体的修改方向或注意事项",
|
||||
"建议2:可能需要考虑的边界情况",
|
||||
"建议3:相关的最佳实践或模式"
|
||||
]
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total": 2,
|
||||
"critical": 1,
|
||||
"high": 1,
|
||||
"medium": 0
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
⚠️ CRITICAL Requirements for modifications field:
|
||||
- old_content: Must be exact text from target file (20-100 chars for unique match)
|
||||
- new_content: Complete replacement text (maintains formatting)
|
||||
- change_type: "update" (replace), "add" (insert), "remove" (delete)
|
||||
- file: Full path relative to project root
|
||||
- section: Markdown heading for context (helps locate position)
|
||||
Return JSON following the schema above. Key requirements:
|
||||
- Minimum 2 strategies per conflict, max 4
|
||||
- All text in Chinese for user-facing fields (brief, name, pros, cons)
|
||||
- modification_suggestions: 2-5 actionable suggestions for custom handling (Chinese)
|
||||
|
||||
Quality Standards:
|
||||
- Each strategy must have actionable modifications
|
||||
- old_content must be precise enough for Edit tool matching
|
||||
- new_content preserves markdown formatting and structure
|
||||
- Recommended strategy (index) based on lowest complexity + risk
|
||||
- modification_suggestions must be specific, actionable, and context-aware
|
||||
- Each suggestion should address a specific aspect (compatibility, migration, testing, etc.)
|
||||
- All text in Chinese for user-facing fields (brief, name, pros, cons, modification_suggestions)
|
||||
- modifications.old_content: 20-100 chars for unique Edit tool matching
|
||||
- modifications.new_content: preserves markdown formatting
|
||||
- modification_suggestions: 2-5 actionable suggestions for custom handling
|
||||
`)
|
||||
```
|
||||
|
||||
@@ -312,143 +206,85 @@ Task(subagent_type="cli-execution-agent", run_in_background=false, prompt=`
|
||||
8. Return execution log path
|
||||
```
|
||||
|
||||
### Phase 3: Iterative User Interaction with Clarification Loop
|
||||
### Phase 3: User Interaction Loop
|
||||
|
||||
**Execution Flow**:
|
||||
```
|
||||
FOR each conflict (逐个处理,无数量限制):
|
||||
clarified = false
|
||||
round = 0
|
||||
userClarifications = []
|
||||
```javascript
|
||||
FOR each conflict:
|
||||
round = 0, clarified = false, userClarifications = []
|
||||
|
||||
WHILE (!clarified && round < 10):
|
||||
round++
|
||||
WHILE (!clarified && round++ < 10):
|
||||
// 1. Display conflict info (text output for context)
|
||||
displayConflictSummary(conflict) // id, brief, severity, overlap_analysis if ModuleOverlap
|
||||
|
||||
// 1. Display conflict (包含所有关键字段)
|
||||
- category, id, brief, severity, description
|
||||
- IF ModuleOverlap: 展示 overlap_analysis
|
||||
* new_module: {name, scenarios, responsibilities}
|
||||
* existing_modules[]: {file, name, scenarios, overlap_scenarios, responsibilities}
|
||||
// 2. Strategy selection via AskUserQuestion
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: formatStrategiesForDisplay(conflict.strategies),
|
||||
header: "策略选择",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
...conflict.strategies.map((s, i) => ({
|
||||
label: `${s.name}${i === conflict.recommended ? ' (推荐)' : ''}`,
|
||||
description: `${s.complexity}复杂度 | ${s.risk}风险${s.clarification_needed?.length ? ' | ⚠️需澄清' : ''}`
|
||||
})),
|
||||
{ label: "自定义修改", description: `建议: ${conflict.modification_suggestions?.slice(0,2).join('; ')}` }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
// 2. Display strategies (2-4个策略 + 自定义选项)
|
||||
- FOR each strategy: {name, approach, complexity, risk, effort, pros, cons}
|
||||
* IF clarification_needed: 展示待澄清问题列表
|
||||
- 自定义选项: {suggestions: modification_suggestions[]}
|
||||
// 3. Handle selection
|
||||
if (userChoice === "自定义修改") {
|
||||
customConflicts.push({ id, brief, category, suggestions, overlap_analysis })
|
||||
break
|
||||
}
|
||||
|
||||
// 3. User selects strategy
|
||||
userChoice = readInput()
|
||||
selectedStrategy = findStrategyByName(userChoice)
|
||||
|
||||
IF userChoice == "自定义":
|
||||
customConflicts.push({id, brief, category, suggestions, overlap_analysis})
|
||||
clarified = true
|
||||
BREAK
|
||||
// 4. Clarification (if needed) - batched max 4 per call
|
||||
if (selectedStrategy.clarification_needed?.length > 0) {
|
||||
for (batch of chunk(selectedStrategy.clarification_needed, 4)) {
|
||||
AskUserQuestion({
|
||||
questions: batch.map((q, i) => ({
|
||||
question: q, header: `澄清${i+1}`, multiSelect: false,
|
||||
options: [{ label: "详细说明", description: "提供答案" }]
|
||||
}))
|
||||
})
|
||||
userClarifications.push(...collectAnswers(batch))
|
||||
}
|
||||
|
||||
selectedStrategy = strategies[userChoice]
|
||||
|
||||
// 4. Clarification loop
|
||||
IF selectedStrategy.clarification_needed.length > 0:
|
||||
// 收集澄清答案
|
||||
FOR each question:
|
||||
answer = readInput()
|
||||
userClarifications.push({question, answer})
|
||||
|
||||
// Agent 重新分析
|
||||
reanalysisResult = Task(cli-execution-agent, prompt={
|
||||
冲突信息: {id, brief, category, 策略}
|
||||
用户澄清: userClarifications[]
|
||||
场景分析: overlap_analysis (if ModuleOverlap)
|
||||
|
||||
输出: {
|
||||
uniqueness_confirmed: bool,
|
||||
rationale: string,
|
||||
updated_strategy: {name, approach, complexity, risk, effort, modifications[]},
|
||||
remaining_questions: [] (如果仍有歧义)
|
||||
}
|
||||
// 5. Agent re-analysis
|
||||
reanalysisResult = Task({
|
||||
subagent_type: "cli-execution-agent",
|
||||
run_in_background: false,
|
||||
prompt: `Conflict: ${conflict.id}, Strategy: ${selectedStrategy.name}
|
||||
User Clarifications: ${JSON.stringify(userClarifications)}
|
||||
Output: { uniqueness_confirmed, rationale, updated_strategy, remaining_questions }`
|
||||
})
|
||||
|
||||
IF reanalysisResult.uniqueness_confirmed:
|
||||
selectedStrategy = updated_strategy
|
||||
selectedStrategy.clarifications = userClarifications
|
||||
if (reanalysisResult.uniqueness_confirmed) {
|
||||
selectedStrategy = { ...reanalysisResult.updated_strategy, clarifications: userClarifications }
|
||||
clarified = true
|
||||
ELSE:
|
||||
// 更新澄清问题,继续下一轮
|
||||
selectedStrategy.clarification_needed = remaining_questions
|
||||
ELSE:
|
||||
} else {
|
||||
selectedStrategy.clarification_needed = reanalysisResult.remaining_questions
|
||||
}
|
||||
} else {
|
||||
clarified = true
|
||||
}
|
||||
|
||||
resolvedConflicts.push({conflict, strategy: selectedStrategy})
|
||||
if (clarified) resolvedConflicts.push({ conflict, strategy: selectedStrategy })
|
||||
END WHILE
|
||||
END FOR
|
||||
|
||||
// Build output
|
||||
selectedStrategies = resolvedConflicts.map(r => ({
|
||||
conflict_id, strategy, clarifications[]
|
||||
conflict_id: r.conflict.id, strategy: r.strategy, clarifications: r.strategy.clarifications || []
|
||||
}))
|
||||
```
|
||||
|
||||
**Key Data Structures**:
|
||||
|
||||
```javascript
|
||||
// Custom conflict tracking
|
||||
customConflicts[] = {
|
||||
id, brief, category,
|
||||
suggestions: modification_suggestions[],
|
||||
overlap_analysis: { new_module{}, existing_modules[] } // ModuleOverlap only
|
||||
}
|
||||
|
||||
// Agent re-analysis prompt output
|
||||
{
|
||||
uniqueness_confirmed: bool,
|
||||
rationale: string,
|
||||
updated_strategy: {
|
||||
name, approach, complexity, risk, effort,
|
||||
modifications: [{file, section, change_type, old_content, new_content, rationale}]
|
||||
},
|
||||
remaining_questions: string[]
|
||||
}
|
||||
```
|
||||
|
||||
**Text Output Example** (展示关键字段):
|
||||
|
||||
```markdown
|
||||
============================================================
|
||||
冲突 1/3 - 第 1 轮
|
||||
============================================================
|
||||
【ModuleOverlap】CON-001: 新增用户认证服务与现有模块功能重叠
|
||||
严重程度: High | 描述: 计划中的 UserAuthService 与现有 AuthManager 场景重叠
|
||||
|
||||
--- 场景重叠分析 ---
|
||||
新模块: UserAuthService | 场景: 登录, Token验证, 权限, MFA
|
||||
现有模块: AuthManager (src/auth/AuthManager.ts) | 重叠: 登录, Token验证
|
||||
|
||||
--- 解决策略 ---
|
||||
1) 合并 (Low复杂度 | Low风险 | 2-3天)
|
||||
⚠️ 需澄清: AuthManager是否能承担MFA?
|
||||
|
||||
2) 拆分边界 (Medium复杂度 | Medium风险 | 4-5天)
|
||||
⚠️ 需澄清: 基础/高级认证边界? Token验证归谁?
|
||||
|
||||
3) 自定义修改
|
||||
建议: 评估扩展性; 策略模式分离; 定义接口边界
|
||||
|
||||
请选择 (1-3): > 2
|
||||
|
||||
--- 澄清问答 (第1轮) ---
|
||||
Q: 基础/高级认证边界?
|
||||
A: 基础=密码登录+token验证, 高级=MFA+OAuth+SSO
|
||||
|
||||
Q: Token验证归谁?
|
||||
A: 统一由 AuthManager 负责
|
||||
|
||||
🔄 重新分析...
|
||||
✅ 唯一性已确认 | 理由: 边界清晰 - AuthManager(基础+token), UserAuthService(MFA+OAuth+SSO)
|
||||
|
||||
============================================================
|
||||
冲突 2/3 - 第 1 轮 [下一个冲突]
|
||||
============================================================
|
||||
```
|
||||
|
||||
**Loop Characteristics**: 逐个处理 | 无限轮次(max 10) | 动态问题生成 | Agent重新分析判断唯一性 | ModuleOverlap场景边界澄清
|
||||
**Key Points**:
|
||||
- AskUserQuestion: max 4 questions/call, batch if more
|
||||
- Strategy options: 2-4 strategies + "自定义修改"
|
||||
- Clarification loop: max 10 rounds, agent判断 uniqueness_confirmed
|
||||
- Custom conflicts: 记录 overlap_analysis 供后续手动处理
|
||||
|
||||
### Phase 4: Apply Modifications
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ allowed-tools: Task(*), Read(*), Glob(*)
|
||||
|
||||
Orchestrator command that invokes `context-search-agent` to gather comprehensive project context for implementation planning. Generates standardized `context-package.json` with codebase analysis, dependencies, and conflict detection.
|
||||
|
||||
**Agent**: `context-search-agent` (`.claude/agents/context-search-agent.md`)
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
@@ -431,6 +430,5 @@ if (historicalConflicts.length > 0 && currentRisk === "low") {
|
||||
|
||||
- **Detection-first**: Always check for existing package before invoking agent
|
||||
- **Project.json integration**: Agent reads `.workflow/project.json` as primary source for project context, avoiding redundant analysis
|
||||
- **Agent autonomy**: Agent handles all discovery logic per `.claude/agents/context-search-agent.md`
|
||||
- **No redundancy**: This command is a thin orchestrator, all logic in agent
|
||||
- **Plan-specific**: Use this for implementation planning; brainstorm mode uses direct agent call
|
||||
|
||||
@@ -354,19 +354,20 @@ Generate task JSON files for ${module.name} module within workflow session
|
||||
IMPORTANT: This is PLANNING ONLY - generate task JSONs, NOT implementing code.
|
||||
IMPORTANT: Generate Task JSONs ONLY. IMPL_PLAN.md and TODO_LIST.md by Phase 3 Coordinator.
|
||||
|
||||
CRITICAL: Follow progressive loading strategy in agent specification
|
||||
CRITICAL: Follow the progressive loading strategy defined in agent specification (load analysis.md files incrementally due to file size)
|
||||
|
||||
## MODULE SCOPE
|
||||
- Module: ${module.name} (${module.type})
|
||||
- Focus Paths: ${module.paths.join(', ')}
|
||||
- Task ID Prefix: IMPL-${module.prefix}
|
||||
- Task Limit: ≤9 tasks
|
||||
- Other Modules: ${otherModules.join(', ')}
|
||||
- Task Limit: ≤9 tasks (hard limit for this module)
|
||||
- Other Modules: ${otherModules.join(', ')} (reference only, do NOT generate tasks for them)
|
||||
|
||||
## SESSION PATHS
|
||||
Input:
|
||||
- Session Metadata: .workflow/active/{session-id}/workflow-session.json
|
||||
- Context Package: .workflow/active/{session-id}/.process/context-package.json
|
||||
|
||||
Output:
|
||||
- Task Dir: .workflow/active/{session-id}/.task/
|
||||
|
||||
@@ -374,21 +375,93 @@ Output:
|
||||
Session ID: {session-id}
|
||||
MCP Capabilities: {exa_code, exa_web, code_index}
|
||||
|
||||
## USER CONFIGURATION (from Phase 0)
|
||||
Execution Method: ${userConfig.executionMethod} // agent|hybrid|cli
|
||||
Preferred CLI Tool: ${userConfig.preferredCliTool} // codex|gemini|qwen|auto
|
||||
Supplementary Materials: ${userConfig.supplementaryMaterials}
|
||||
|
||||
## CLI TOOL SELECTION
|
||||
Based on userConfig.executionMethod:
|
||||
- "agent": No command field in implementation_approach steps
|
||||
- "hybrid": Add command field to complex steps only (agent handles simple steps)
|
||||
- "cli": Add command field to ALL implementation_approach steps
|
||||
|
||||
CLI Resume Support (MANDATORY for all CLI commands):
|
||||
- Use --resume parameter to continue from previous task execution
|
||||
- Read previous task's cliExecutionId from session state
|
||||
- Format: ccw cli -p "[prompt]" --resume ${previousCliId} --tool ${tool} --mode write
|
||||
|
||||
## EXPLORATION CONTEXT (from context-package.exploration_results)
|
||||
- Load exploration_results from context-package.json
|
||||
- Filter for ${module.name} module: Use aggregated_insights.critical_files matching ${module.paths.join(', ')}
|
||||
- Apply module-relevant constraints from aggregated_insights.constraints
|
||||
- Reference aggregated_insights.all_patterns applicable to ${module.name}
|
||||
- Use aggregated_insights.all_integration_points for precise modification locations within module scope
|
||||
- Use conflict_indicators for risk-aware task sequencing
|
||||
|
||||
## CONFLICT RESOLUTION CONTEXT (if exists)
|
||||
- Check context-package.conflict_detection.resolution_file for conflict-resolution.json path
|
||||
- If exists, load .process/conflict-resolution.json:
|
||||
- Apply planning_constraints relevant to ${module.name} as task constraints
|
||||
- Reference resolved_conflicts affecting ${module.name} for implementation approach alignment
|
||||
- Handle custom_conflicts with explicit task notes
|
||||
|
||||
## CROSS-MODULE DEPENDENCIES
|
||||
- Use placeholder: depends_on: ["CROSS::{module}::{pattern}"]
|
||||
- Example: depends_on: ["CROSS::B::api-endpoint"]
|
||||
- For dependencies ON other modules: Use placeholder depends_on: ["CROSS::{module}::{pattern}"]
|
||||
- Example: depends_on: ["CROSS::B::api-endpoint"] (this module depends on B's api-endpoint task)
|
||||
- Phase 3 Coordinator resolves to actual task IDs
|
||||
- For dependencies FROM other modules: Document in task context as "provides_for" annotation
|
||||
|
||||
## EXPECTED DELIVERABLES
|
||||
Task JSON Files (.task/IMPL-${module.prefix}*.json):
|
||||
- 6-field schema per agent specification
|
||||
- 6-field schema (id, title, status, context_package_path, meta, context, flow_control)
|
||||
- Task ID format: IMPL-${module.prefix}1, IMPL-${module.prefix}2, ...
|
||||
- Quantified requirements with explicit counts
|
||||
- Artifacts integration from context package (filtered for ${module.name})
|
||||
- **focus_paths enhanced with exploration critical_files (module-scoped)**
|
||||
- Flow control with pre_analysis steps (include exploration integration_points analysis)
|
||||
- **CLI Execution IDs and strategies (MANDATORY)**
|
||||
- Focus ONLY on ${module.name} module scope
|
||||
|
||||
## CLI EXECUTION ID REQUIREMENTS (MANDATORY)
|
||||
Each task JSON MUST include:
|
||||
- **cli_execution_id**: Unique ID for CLI execution (format: `{session_id}-IMPL-${module.prefix}{seq}`)
|
||||
- **cli_execution**: Strategy object based on depends_on:
|
||||
- No deps → `{ "strategy": "new" }`
|
||||
- 1 dep (single child) → `{ "strategy": "resume", "resume_from": "parent-cli-id" }`
|
||||
- 1 dep (multiple children) → `{ "strategy": "fork", "resume_from": "parent-cli-id" }`
|
||||
- N deps → `{ "strategy": "merge_fork", "merge_from": ["id1", "id2", ...] }`
|
||||
- Cross-module dep → `{ "strategy": "cross_module_fork", "resume_from": "CROSS::{module}::{pattern}" }`
|
||||
|
||||
**CLI Execution Strategy Rules**:
|
||||
1. **new**: Task has no dependencies - starts fresh CLI conversation
|
||||
2. **resume**: Task has 1 parent AND that parent has only this child - continues same conversation
|
||||
3. **fork**: Task has 1 parent BUT parent has multiple children - creates new branch with parent context
|
||||
4. **merge_fork**: Task has multiple parents - merges all parent contexts into new conversation
|
||||
5. **cross_module_fork**: Task depends on task from another module - Phase 3 resolves placeholder
|
||||
|
||||
**Execution Command Patterns**:
|
||||
- new: `ccw cli -p "[prompt]" --tool [tool] --mode write --id [cli_execution_id]`
|
||||
- resume: `ccw cli -p "[prompt]" --resume [resume_from] --tool [tool] --mode write`
|
||||
- fork: `ccw cli -p "[prompt]" --resume [resume_from] --id [cli_execution_id] --tool [tool] --mode write`
|
||||
- merge_fork: `ccw cli -p "[prompt]" --resume [merge_from.join(',')] --id [cli_execution_id] --tool [tool] --mode write`
|
||||
- cross_module_fork: (Phase 3 resolves placeholder, then uses fork pattern)
|
||||
|
||||
## QUALITY STANDARDS
|
||||
Hard Constraints:
|
||||
- Task count <= 9 for this module (hard limit - coordinate with Phase 3 if exceeded)
|
||||
- All requirements quantified (explicit counts and enumerated lists)
|
||||
- Acceptance criteria measurable (include verification commands)
|
||||
- Artifact references mapped from context package (module-scoped filter)
|
||||
- Focus paths use absolute paths or clear relative paths from project root
|
||||
- Cross-module dependencies use CROSS:: placeholder format
|
||||
|
||||
## SUCCESS CRITERIA
|
||||
- Task JSONs saved to .task/ with IMPL-${module.prefix}* naming
|
||||
- Cross-module dependencies use CROSS:: placeholder format
|
||||
- Return task count and brief summary
|
||||
- All task JSONs include cli_execution_id and cli_execution strategy
|
||||
- Cross-module dependencies use CROSS:: placeholder format consistently
|
||||
- Focus paths scoped to ${module.paths.join(', ')} only
|
||||
- Return: task count, task IDs, dependency summary (internal + cross-module)
|
||||
`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -239,15 +239,6 @@ If conflict_risk was medium/high, modifications have been applied to:
|
||||
|
||||
**Agent Configuration Reference**: All TDD task generation rules, quantification requirements, Red-Green-Refactor cycle structure, quality standards, and execution details are defined in action-planning-agent.
|
||||
|
||||
Refer to: @.claude/agents/action-planning-agent.md for:
|
||||
- TDD Task Decomposition Standards
|
||||
- Red-Green-Refactor Cycle Requirements
|
||||
- Quantification Requirements (MANDATORY)
|
||||
- 5-Field Task JSON Schema
|
||||
- IMPL_PLAN.md Structure (TDD variant)
|
||||
- TODO_LIST.md Format
|
||||
- TDD Execution Flow & Quality Validation
|
||||
|
||||
### TDD-Specific Requirements Summary
|
||||
|
||||
#### Task Structure Philosophy
|
||||
|
||||
@@ -14,7 +14,7 @@ allowed-tools: Task(*), Read(*), Glob(*)
|
||||
|
||||
Orchestrator command that invokes `test-context-search-agent` to gather comprehensive test coverage context for test generation workflows. Generates standardized `test-context-package.json` with coverage analysis, framework detection, and source implementation context.
|
||||
|
||||
**Agent**: `test-context-search-agent` (`.claude/agents/test-context-search-agent.md`)
|
||||
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
@@ -89,7 +89,6 @@ Task(
|
||||
run_in_background=false,
|
||||
description="Gather test coverage context",
|
||||
prompt=`
|
||||
You are executing as test-context-search-agent (.claude/agents/test-context-search-agent.md).
|
||||
|
||||
## Execution Mode
|
||||
**PLAN MODE** (Comprehensive) - Full Phase 1-3 execution
|
||||
@@ -229,7 +228,7 @@ Refer to `test-context-search-agent.md` Phase 3.2 for complete `test-context-pac
|
||||
## Notes
|
||||
|
||||
- **Detection-first**: Always check for existing test-context-package before invoking agent
|
||||
- **Agent autonomy**: Agent handles all coverage analysis logic per `.claude/agents/test-context-search-agent.md`
|
||||
|
||||
- **No redundancy**: This command is a thin orchestrator, all logic in agent
|
||||
- **Framework agnostic**: Supports Jest, Mocha, pytest, RSpec, Go testing, etc.
|
||||
- **Coverage focus**: Primary goal is identifying implementation files without tests
|
||||
|
||||
@@ -107,8 +107,6 @@ CRITICAL:
|
||||
- Follow the progressive loading strategy defined in your agent specification (load context incrementally from memory-first approach)
|
||||
|
||||
## AGENT CONFIGURATION REFERENCE
|
||||
All test task generation rules, schemas, and quality standards are defined in your agent specification:
|
||||
@.claude/agents/action-planning-agent.md
|
||||
|
||||
Refer to your specification for:
|
||||
- Test Task JSON Schema (6-field structure with test-specific metadata)
|
||||
|
||||
584
.claude/skills/_shared/mermaid-utils.md
Normal file
584
.claude/skills/_shared/mermaid-utils.md
Normal file
@@ -0,0 +1,584 @@
|
||||
# Mermaid Utilities Library
|
||||
|
||||
Shared utilities for generating and validating Mermaid diagrams across all analysis skills.
|
||||
|
||||
## Sanitization Functions
|
||||
|
||||
### sanitizeId
|
||||
|
||||
Convert any text to a valid Mermaid node ID.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Sanitize text to valid Mermaid node ID
|
||||
* - Only alphanumeric and underscore allowed
|
||||
* - Cannot start with number
|
||||
* - Truncates to 50 chars max
|
||||
*
|
||||
* @param {string} text - Input text
|
||||
* @returns {string} - Valid Mermaid ID
|
||||
*/
|
||||
function sanitizeId(text) {
|
||||
if (!text) return '_empty';
|
||||
return text
|
||||
.replace(/[^a-zA-Z0-9_\u4e00-\u9fa5]/g, '_') // Allow Chinese chars
|
||||
.replace(/^[0-9]/, '_$&') // Prefix number with _
|
||||
.replace(/_+/g, '_') // Collapse multiple _
|
||||
.substring(0, 50); // Limit length
|
||||
}
|
||||
|
||||
// Examples:
|
||||
// sanitizeId("User-Service") → "User_Service"
|
||||
// sanitizeId("3rdParty") → "_3rdParty"
|
||||
// sanitizeId("用户服务") → "用户服务"
|
||||
```
|
||||
|
||||
### escapeLabel
|
||||
|
||||
Escape special characters for Mermaid labels.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Escape special characters in Mermaid labels
|
||||
* Uses HTML entity encoding for problematic chars
|
||||
*
|
||||
* @param {string} text - Label text
|
||||
* @returns {string} - Escaped label
|
||||
*/
|
||||
function escapeLabel(text) {
|
||||
if (!text) return '';
|
||||
return text
|
||||
.replace(/"/g, "'") // Avoid quote issues
|
||||
.replace(/\(/g, '#40;') // (
|
||||
.replace(/\)/g, '#41;') // )
|
||||
.replace(/\{/g, '#123;') // {
|
||||
.replace(/\}/g, '#125;') // }
|
||||
.replace(/\[/g, '#91;') // [
|
||||
.replace(/\]/g, '#93;') // ]
|
||||
.replace(/</g, '#60;') // <
|
||||
.replace(/>/g, '#62;') // >
|
||||
.replace(/\|/g, '#124;') // |
|
||||
.substring(0, 80); // Limit length
|
||||
}
|
||||
|
||||
// Examples:
|
||||
// escapeLabel("Process(data)") → "Process#40;data#41;"
|
||||
// escapeLabel("Check {valid?}") → "Check #123;valid?#125;"
|
||||
```
|
||||
|
||||
### sanitizeType
|
||||
|
||||
Sanitize type names for class diagrams.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Sanitize type names for Mermaid classDiagram
|
||||
* Removes generics syntax that causes issues
|
||||
*
|
||||
* @param {string} type - Type name
|
||||
* @returns {string} - Sanitized type
|
||||
*/
|
||||
function sanitizeType(type) {
|
||||
if (!type) return 'any';
|
||||
return type
|
||||
.replace(/<[^>]*>/g, '') // Remove generics <T>
|
||||
.replace(/\|/g, ' or ') // Union types
|
||||
.replace(/&/g, ' and ') // Intersection types
|
||||
.replace(/\[\]/g, 'Array') // Array notation
|
||||
.substring(0, 30);
|
||||
}
|
||||
|
||||
// Examples:
|
||||
// sanitizeType("Array<string>") → "Array"
|
||||
// sanitizeType("string | number") → "string or number"
|
||||
```
|
||||
|
||||
## Diagram Generation Functions
|
||||
|
||||
### generateFlowchartNode
|
||||
|
||||
Generate a flowchart node with proper shape.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Generate flowchart node with shape
|
||||
*
|
||||
* @param {string} id - Node ID
|
||||
* @param {string} label - Display label
|
||||
* @param {string} type - Node type: start|end|process|decision|io|subroutine
|
||||
* @returns {string} - Mermaid node definition
|
||||
*/
|
||||
function generateFlowchartNode(id, label, type = 'process') {
|
||||
const safeId = sanitizeId(id);
|
||||
const safeLabel = escapeLabel(label);
|
||||
|
||||
const shapes = {
|
||||
start: `${safeId}(["${safeLabel}"])`, // Stadium shape
|
||||
end: `${safeId}(["${safeLabel}"])`, // Stadium shape
|
||||
process: `${safeId}["${safeLabel}"]`, // Rectangle
|
||||
decision: `${safeId}{"${safeLabel}"}`, // Diamond
|
||||
io: `${safeId}[/"${safeLabel}"/]`, // Parallelogram
|
||||
subroutine: `${safeId}[["${safeLabel}"]]`, // Subroutine
|
||||
database: `${safeId}[("${safeLabel}")]`, // Cylinder
|
||||
manual: `${safeId}[/"${safeLabel}"\\]` // Trapezoid
|
||||
};
|
||||
|
||||
return shapes[type] || shapes.process;
|
||||
}
|
||||
```
|
||||
|
||||
### generateFlowchartEdge
|
||||
|
||||
Generate a flowchart edge with optional label.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Generate flowchart edge
|
||||
*
|
||||
* @param {string} from - Source node ID
|
||||
* @param {string} to - Target node ID
|
||||
* @param {string} label - Edge label (optional)
|
||||
* @param {string} style - Edge style: solid|dashed|thick
|
||||
* @returns {string} - Mermaid edge definition
|
||||
*/
|
||||
function generateFlowchartEdge(from, to, label = '', style = 'solid') {
|
||||
const safeFrom = sanitizeId(from);
|
||||
const safeTo = sanitizeId(to);
|
||||
const safeLabel = label ? `|"${escapeLabel(label)}"|` : '';
|
||||
|
||||
const arrows = {
|
||||
solid: '-->',
|
||||
dashed: '-.->',
|
||||
thick: '==>'
|
||||
};
|
||||
|
||||
const arrow = arrows[style] || arrows.solid;
|
||||
return ` ${safeFrom} ${arrow}${safeLabel} ${safeTo}`;
|
||||
}
|
||||
```
|
||||
|
||||
### generateAlgorithmFlowchart (Enhanced)
|
||||
|
||||
Generate algorithm flowchart with branch/loop support.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Generate algorithm flowchart with decision support
|
||||
*
|
||||
* @param {Object} algorithm - Algorithm definition
|
||||
* - name: Algorithm name
|
||||
* - inputs: [{name, type}]
|
||||
* - outputs: [{name, type}]
|
||||
* - steps: [{id, description, type, next: [id], conditions: [text]}]
|
||||
* @returns {string} - Complete Mermaid flowchart
|
||||
*/
|
||||
function generateAlgorithmFlowchart(algorithm) {
|
||||
let mermaid = 'flowchart TD\n';
|
||||
|
||||
// Start node
|
||||
mermaid += ` START(["开始: ${escapeLabel(algorithm.name)}"])\n`;
|
||||
|
||||
// Input node (if has inputs)
|
||||
if (algorithm.inputs?.length > 0) {
|
||||
const inputList = algorithm.inputs.map(i => `${i.name}: ${i.type}`).join(', ');
|
||||
mermaid += ` INPUT[/"输入: ${escapeLabel(inputList)}"/]\n`;
|
||||
mermaid += ` START --> INPUT\n`;
|
||||
}
|
||||
|
||||
// Process nodes
|
||||
const steps = algorithm.steps || [];
|
||||
for (const step of steps) {
|
||||
const nodeId = sanitizeId(step.id || `STEP_${step.step_num}`);
|
||||
|
||||
if (step.type === 'decision') {
|
||||
mermaid += ` ${nodeId}{"${escapeLabel(step.description)}"}\n`;
|
||||
} else if (step.type === 'io') {
|
||||
mermaid += ` ${nodeId}[/"${escapeLabel(step.description)}"/]\n`;
|
||||
} else if (step.type === 'loop_start') {
|
||||
mermaid += ` ${nodeId}[["循环: ${escapeLabel(step.description)}"]]\n`;
|
||||
} else {
|
||||
mermaid += ` ${nodeId}["${escapeLabel(step.description)}"]\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Output node
|
||||
const outputDesc = algorithm.outputs?.map(o => o.name).join(', ') || '结果';
|
||||
mermaid += ` OUTPUT[/"输出: ${escapeLabel(outputDesc)}"/]\n`;
|
||||
mermaid += ` END_(["结束"])\n`;
|
||||
|
||||
// Connect first step to input/start
|
||||
if (steps.length > 0) {
|
||||
const firstStep = sanitizeId(steps[0].id || 'STEP_1');
|
||||
if (algorithm.inputs?.length > 0) {
|
||||
mermaid += ` INPUT --> ${firstStep}\n`;
|
||||
} else {
|
||||
mermaid += ` START --> ${firstStep}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect steps based on next array
|
||||
for (const step of steps) {
|
||||
const nodeId = sanitizeId(step.id || `STEP_${step.step_num}`);
|
||||
|
||||
if (step.next && step.next.length > 0) {
|
||||
step.next.forEach((nextId, index) => {
|
||||
const safeNextId = sanitizeId(nextId);
|
||||
const condition = step.conditions?.[index];
|
||||
|
||||
if (condition) {
|
||||
mermaid += ` ${nodeId} -->|"${escapeLabel(condition)}"| ${safeNextId}\n`;
|
||||
} else {
|
||||
mermaid += ` ${nodeId} --> ${safeNextId}\n`;
|
||||
}
|
||||
});
|
||||
} else if (!step.type?.includes('end')) {
|
||||
// Default: connect to next step or output
|
||||
const stepIndex = steps.indexOf(step);
|
||||
if (stepIndex < steps.length - 1) {
|
||||
const nextStep = sanitizeId(steps[stepIndex + 1].id || `STEP_${stepIndex + 2}`);
|
||||
mermaid += ` ${nodeId} --> ${nextStep}\n`;
|
||||
} else {
|
||||
mermaid += ` ${nodeId} --> OUTPUT\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect output to end
|
||||
mermaid += ` OUTPUT --> END_\n`;
|
||||
|
||||
return mermaid;
|
||||
}
|
||||
```
|
||||
|
||||
## Diagram Validation
|
||||
|
||||
### validateMermaidSyntax
|
||||
|
||||
Comprehensive Mermaid syntax validation.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validate Mermaid diagram syntax
|
||||
*
|
||||
* @param {string} content - Mermaid diagram content
|
||||
* @returns {Object} - {valid: boolean, issues: string[]}
|
||||
*/
|
||||
function validateMermaidSyntax(content) {
|
||||
const issues = [];
|
||||
|
||||
// Check 1: Diagram type declaration
|
||||
if (!content.match(/^(graph|flowchart|classDiagram|sequenceDiagram|stateDiagram|erDiagram|gantt|pie|mindmap)/m)) {
|
||||
issues.push('Missing diagram type declaration');
|
||||
}
|
||||
|
||||
// Check 2: Undefined values
|
||||
if (content.includes('undefined') || content.includes('null')) {
|
||||
issues.push('Contains undefined/null values');
|
||||
}
|
||||
|
||||
// Check 3: Invalid arrow syntax
|
||||
if (content.match(/-->\s*-->/)) {
|
||||
issues.push('Double arrow syntax error');
|
||||
}
|
||||
|
||||
// Check 4: Unescaped special characters in labels
|
||||
const labelMatches = content.match(/\["[^"]*[(){}[\]<>][^"]*"\]/g);
|
||||
if (labelMatches?.some(m => !m.includes('#'))) {
|
||||
issues.push('Unescaped special characters in labels');
|
||||
}
|
||||
|
||||
// Check 5: Node ID starts with number
|
||||
if (content.match(/\n\s*[0-9][a-zA-Z0-9_]*[\[\({]/)) {
|
||||
issues.push('Node ID cannot start with number');
|
||||
}
|
||||
|
||||
// Check 6: Nested subgraph syntax error
|
||||
if (content.match(/subgraph\s+\S+\s*\n[^e]*subgraph/)) {
|
||||
// This is actually valid, only flag if brackets don't match
|
||||
const subgraphCount = (content.match(/subgraph/g) || []).length;
|
||||
const endCount = (content.match(/\bend\b/g) || []).length;
|
||||
if (subgraphCount > endCount) {
|
||||
issues.push('Unbalanced subgraph/end blocks');
|
||||
}
|
||||
}
|
||||
|
||||
// Check 7: Invalid arrow type for diagram type
|
||||
const diagramType = content.match(/^(graph|flowchart|classDiagram|sequenceDiagram)/m)?.[1];
|
||||
if (diagramType === 'classDiagram' && content.includes('-->|')) {
|
||||
issues.push('Invalid edge label syntax for classDiagram');
|
||||
}
|
||||
|
||||
// Check 8: Empty node labels
|
||||
if (content.match(/\[""\]|\{\}|\(\)/)) {
|
||||
issues.push('Empty node labels detected');
|
||||
}
|
||||
|
||||
// Check 9: Reserved keywords as IDs
|
||||
const reserved = ['end', 'graph', 'subgraph', 'direction', 'class', 'click'];
|
||||
for (const keyword of reserved) {
|
||||
const pattern = new RegExp(`\\n\\s*${keyword}\\s*[\\[\\(\\{]`, 'i');
|
||||
if (content.match(pattern)) {
|
||||
issues.push(`Reserved keyword "${keyword}" used as node ID`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check 10: Line length (Mermaid has issues with very long lines)
|
||||
const lines = content.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].length > 500) {
|
||||
issues.push(`Line ${i + 1} exceeds 500 characters`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valid: issues.length === 0,
|
||||
issues
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### validateDiagramDirectory
|
||||
|
||||
Validate all diagrams in a directory.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validate all Mermaid diagrams in directory
|
||||
*
|
||||
* @param {string} diagramDir - Path to diagrams directory
|
||||
* @returns {Object[]} - Array of {file, valid, issues}
|
||||
*/
|
||||
function validateDiagramDirectory(diagramDir) {
|
||||
const files = Glob(`${diagramDir}/*.mmd`);
|
||||
const results = [];
|
||||
|
||||
for (const file of files) {
|
||||
const content = Read(file);
|
||||
const validation = validateMermaidSyntax(content);
|
||||
|
||||
results.push({
|
||||
file: file.split('/').pop(),
|
||||
path: file,
|
||||
valid: validation.valid,
|
||||
issues: validation.issues,
|
||||
lines: content.split('\n').length
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
```
|
||||
|
||||
## Class Diagram Utilities
|
||||
|
||||
### generateClassDiagram
|
||||
|
||||
Generate class diagram with relationships.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Generate class diagram from analysis data
|
||||
*
|
||||
* @param {Object} analysis - Data structure analysis
|
||||
* - entities: [{name, type, properties, methods}]
|
||||
* - relationships: [{from, to, type, label}]
|
||||
* @param {Object} options - Generation options
|
||||
* - maxClasses: Max classes to include (default: 15)
|
||||
* - maxProperties: Max properties per class (default: 8)
|
||||
* - maxMethods: Max methods per class (default: 6)
|
||||
* @returns {string} - Mermaid classDiagram
|
||||
*/
|
||||
function generateClassDiagram(analysis, options = {}) {
|
||||
const maxClasses = options.maxClasses || 15;
|
||||
const maxProperties = options.maxProperties || 8;
|
||||
const maxMethods = options.maxMethods || 6;
|
||||
|
||||
let mermaid = 'classDiagram\n';
|
||||
|
||||
const entities = (analysis.entities || []).slice(0, maxClasses);
|
||||
|
||||
// Generate classes
|
||||
for (const entity of entities) {
|
||||
const className = sanitizeId(entity.name);
|
||||
mermaid += ` class ${className} {\n`;
|
||||
|
||||
// Properties
|
||||
for (const prop of (entity.properties || []).slice(0, maxProperties)) {
|
||||
const vis = {public: '+', private: '-', protected: '#'}[prop.visibility] || '+';
|
||||
const type = sanitizeType(prop.type);
|
||||
mermaid += ` ${vis}${type} ${prop.name}\n`;
|
||||
}
|
||||
|
||||
// Methods
|
||||
for (const method of (entity.methods || []).slice(0, maxMethods)) {
|
||||
const vis = {public: '+', private: '-', protected: '#'}[method.visibility] || '+';
|
||||
const params = (method.params || []).map(p => p.name).join(', ');
|
||||
const returnType = sanitizeType(method.returnType || 'void');
|
||||
mermaid += ` ${vis}${method.name}(${params}) ${returnType}\n`;
|
||||
}
|
||||
|
||||
mermaid += ' }\n';
|
||||
|
||||
// Add stereotype if applicable
|
||||
if (entity.type === 'interface') {
|
||||
mermaid += ` <<interface>> ${className}\n`;
|
||||
} else if (entity.type === 'abstract') {
|
||||
mermaid += ` <<abstract>> ${className}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate relationships
|
||||
const arrows = {
|
||||
inheritance: '--|>',
|
||||
implementation: '..|>',
|
||||
composition: '*--',
|
||||
aggregation: 'o--',
|
||||
association: '-->',
|
||||
dependency: '..>'
|
||||
};
|
||||
|
||||
for (const rel of (analysis.relationships || [])) {
|
||||
const from = sanitizeId(rel.from);
|
||||
const to = sanitizeId(rel.to);
|
||||
const arrow = arrows[rel.type] || '-->';
|
||||
const label = rel.label ? ` : ${escapeLabel(rel.label)}` : '';
|
||||
|
||||
// Only include if both entities exist
|
||||
if (entities.some(e => sanitizeId(e.name) === from) &&
|
||||
entities.some(e => sanitizeId(e.name) === to)) {
|
||||
mermaid += ` ${from} ${arrow} ${to}${label}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return mermaid;
|
||||
}
|
||||
```
|
||||
|
||||
## Sequence Diagram Utilities
|
||||
|
||||
### generateSequenceDiagram
|
||||
|
||||
Generate sequence diagram from scenario.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Generate sequence diagram from scenario
|
||||
*
|
||||
* @param {Object} scenario - Sequence scenario
|
||||
* - name: Scenario name
|
||||
* - actors: [{id, name, type}]
|
||||
* - messages: [{from, to, description, type}]
|
||||
* - blocks: [{type, condition, messages}]
|
||||
* @returns {string} - Mermaid sequenceDiagram
|
||||
*/
|
||||
function generateSequenceDiagram(scenario) {
|
||||
let mermaid = 'sequenceDiagram\n';
|
||||
|
||||
// Title
|
||||
if (scenario.name) {
|
||||
mermaid += ` title ${escapeLabel(scenario.name)}\n`;
|
||||
}
|
||||
|
||||
// Participants
|
||||
for (const actor of scenario.actors || []) {
|
||||
const actorType = actor.type === 'external' ? 'actor' : 'participant';
|
||||
mermaid += ` ${actorType} ${sanitizeId(actor.id)} as ${escapeLabel(actor.name)}\n`;
|
||||
}
|
||||
|
||||
mermaid += '\n';
|
||||
|
||||
// Messages
|
||||
for (const msg of scenario.messages || []) {
|
||||
const from = sanitizeId(msg.from);
|
||||
const to = sanitizeId(msg.to);
|
||||
const desc = escapeLabel(msg.description);
|
||||
|
||||
let arrow;
|
||||
switch (msg.type) {
|
||||
case 'async': arrow = '->>'; break;
|
||||
case 'response': arrow = '-->>'; break;
|
||||
case 'create': arrow = '->>+'; break;
|
||||
case 'destroy': arrow = '->>-'; break;
|
||||
case 'self': arrow = '->>'; break;
|
||||
default: arrow = '->>';
|
||||
}
|
||||
|
||||
mermaid += ` ${from}${arrow}${to}: ${desc}\n`;
|
||||
|
||||
// Activation
|
||||
if (msg.activate) {
|
||||
mermaid += ` activate ${to}\n`;
|
||||
}
|
||||
if (msg.deactivate) {
|
||||
mermaid += ` deactivate ${from}\n`;
|
||||
}
|
||||
|
||||
// Notes
|
||||
if (msg.note) {
|
||||
mermaid += ` Note over ${to}: ${escapeLabel(msg.note)}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks (loops, alt, opt)
|
||||
for (const block of scenario.blocks || []) {
|
||||
switch (block.type) {
|
||||
case 'loop':
|
||||
mermaid += ` loop ${escapeLabel(block.condition)}\n`;
|
||||
break;
|
||||
case 'alt':
|
||||
mermaid += ` alt ${escapeLabel(block.condition)}\n`;
|
||||
break;
|
||||
case 'opt':
|
||||
mermaid += ` opt ${escapeLabel(block.condition)}\n`;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const m of block.messages || []) {
|
||||
mermaid += ` ${sanitizeId(m.from)}->>${sanitizeId(m.to)}: ${escapeLabel(m.description)}\n`;
|
||||
}
|
||||
|
||||
mermaid += ' end\n';
|
||||
}
|
||||
|
||||
return mermaid;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Algorithm with Branches
|
||||
|
||||
```javascript
|
||||
const algorithm = {
|
||||
name: "用户认证流程",
|
||||
inputs: [{name: "credentials", type: "Object"}],
|
||||
outputs: [{name: "token", type: "JWT"}],
|
||||
steps: [
|
||||
{id: "validate", description: "验证输入格式", type: "process"},
|
||||
{id: "check_user", description: "用户是否存在?", type: "decision",
|
||||
next: ["verify_pwd", "error_user"], conditions: ["是", "否"]},
|
||||
{id: "verify_pwd", description: "验证密码", type: "process"},
|
||||
{id: "pwd_ok", description: "密码正确?", type: "decision",
|
||||
next: ["gen_token", "error_pwd"], conditions: ["是", "否"]},
|
||||
{id: "gen_token", description: "生成 JWT Token", type: "process"},
|
||||
{id: "error_user", description: "返回用户不存在", type: "io"},
|
||||
{id: "error_pwd", description: "返回密码错误", type: "io"}
|
||||
]
|
||||
};
|
||||
|
||||
const flowchart = generateAlgorithmFlowchart(algorithm);
|
||||
```
|
||||
|
||||
### Example 2: Validate Before Output
|
||||
|
||||
```javascript
|
||||
const diagram = generateClassDiagram(analysis);
|
||||
const validation = validateMermaidSyntax(diagram);
|
||||
|
||||
if (!validation.valid) {
|
||||
console.log("Diagram has issues:", validation.issues);
|
||||
// Fix issues or regenerate
|
||||
} else {
|
||||
Write(`${outputDir}/class-diagram.mmd`, diagram);
|
||||
}
|
||||
```
|
||||
@@ -806,8 +806,6 @@ Use `analysis_results.complexity` or task count to determine structure:
|
||||
**Examples**:
|
||||
- GOOD: `"Implement 5 commands: [cmd1, cmd2, cmd3, cmd4, cmd5]"`
|
||||
- BAD: `"Implement new commands"`
|
||||
- GOOD: `"5 files created: verify by ls .claude/commands/*.md | wc -l = 5"`
|
||||
- BAD: `"All commands implemented successfully"`
|
||||
|
||||
### 3.2 Planning & Organization Standards
|
||||
|
||||
|
||||
@@ -400,7 +400,7 @@ Task(subagent_type="{meta.agent}",
|
||||
1. Read complete task JSON: {session.task_json_path}
|
||||
2. Load context package: {session.context_package_path}
|
||||
|
||||
Follow complete execution guidelines in @.claude/agents/{meta.agent}.md
|
||||
|
||||
|
||||
**Session Paths**:
|
||||
- Workflow Dir: {session.workflow_dir}
|
||||
|
||||
@@ -15,7 +15,6 @@ allowed-tools: Task(*), Read(*), Glob(*)
|
||||
|
||||
Orchestrator command that invokes `context-search-agent` to gather comprehensive project context for implementation planning. Generates standardized `context-package.json` with codebase analysis, dependencies, and conflict detection.
|
||||
|
||||
**Agent**: `context-search-agent` (`.claude/agents/context-search-agent.md`)
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
@@ -429,6 +428,6 @@ if (historicalConflicts.length > 0 && currentRisk === "low") {
|
||||
|
||||
- **Detection-first**: Always check for existing package before invoking agent
|
||||
- **Project.json integration**: Agent reads `.workflow/project.json` as primary source for project context, avoiding redundant analysis
|
||||
- **Agent autonomy**: Agent handles all discovery logic per `.claude/agents/context-search-agent.md`
|
||||
|
||||
- **No redundancy**: This command is a thin orchestrator, all logic in agent
|
||||
- **Plan-specific**: Use this for implementation planning; brainstorm mode uses direct agent call
|
||||
|
||||
@@ -238,14 +238,7 @@ If conflict_risk was medium/high, modifications have been applied to:
|
||||
|
||||
**Agent Configuration Reference**: All TDD task generation rules, quantification requirements, Red-Green-Refactor cycle structure, quality standards, and execution details are defined in action-planning-agent.
|
||||
|
||||
Refer to: @.claude/agents/action-planning-agent.md for:
|
||||
- TDD Task Decomposition Standards
|
||||
- Red-Green-Refactor Cycle Requirements
|
||||
- Quantification Requirements (MANDATORY)
|
||||
- 5-Field Task JSON Schema
|
||||
- IMPL_PLAN.md Structure (TDD variant)
|
||||
- TODO_LIST.md Format
|
||||
- TDD Execution Flow & Quality Validation
|
||||
|
||||
|
||||
### TDD-Specific Requirements Summary
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ allowed-tools: Task(*), Read(*), Glob(*)
|
||||
|
||||
Orchestrator command that invokes `test-context-search-agent` to gather comprehensive test coverage context for test generation workflows. Generates standardized `test-context-package.json` with coverage analysis, framework detection, and source implementation context.
|
||||
|
||||
**Agent**: `test-context-search-agent` (`.claude/agents/test-context-search-agent.md`)
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
- **Agent Delegation**: Delegate all test coverage analysis to `test-context-search-agent` for autonomous execution
|
||||
@@ -88,7 +86,6 @@ Task(
|
||||
subagent_type="test-context-search-agent",
|
||||
description="Gather test coverage context",
|
||||
prompt=`
|
||||
You are executing as test-context-search-agent (.claude/agents/test-context-search-agent.md).
|
||||
|
||||
## Execution Mode
|
||||
**PLAN MODE** (Comprehensive) - Full Phase 1-3 execution
|
||||
@@ -228,7 +225,7 @@ Refer to `test-context-search-agent.md` Phase 3.2 for complete `test-context-pac
|
||||
## Notes
|
||||
|
||||
- **Detection-first**: Always check for existing test-context-package before invoking agent
|
||||
- **Agent autonomy**: Agent handles all coverage analysis logic per `.claude/agents/test-context-search-agent.md`
|
||||
|
||||
- **No redundancy**: This command is a thin orchestrator, all logic in agent
|
||||
- **Framework agnostic**: Supports Jest, Mocha, pytest, RSpec, Go testing, etc.
|
||||
- **Coverage focus**: Primary goal is identifying implementation files without tests
|
||||
|
||||
@@ -106,8 +106,6 @@ CRITICAL:
|
||||
- Follow the progressive loading strategy defined in your agent specification (load context incrementally from memory-first approach)
|
||||
|
||||
## AGENT CONFIGURATION REFERENCE
|
||||
All test task generation rules, schemas, and quality standards are defined in your agent specification:
|
||||
@.claude/agents/action-planning-agent.md
|
||||
|
||||
Refer to your specification for:
|
||||
- Test Task JSON Schema (6-field structure with test-specific metadata)
|
||||
|
||||
132
.claude/skills/copyright-docs/SKILL.md
Normal file
132
.claude/skills/copyright-docs/SKILL.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: copyright-docs
|
||||
description: Generate software copyright design specification documents compliant with China Copyright Protection Center (CPCC) standards. Creates complete design documents with Mermaid diagrams based on source code analysis. Use for software copyright registration, generating design specification, creating CPCC-compliant documents, or documenting software for intellectual property protection. Triggers on "软件著作权", "设计说明书", "版权登记", "CPCC", "软著申请".
|
||||
allowed-tools: Task, AskUserQuestion, Read, Bash, Glob, Grep, Write
|
||||
---
|
||||
|
||||
# Software Copyright Documentation Skill
|
||||
|
||||
Generate CPCC-compliant software design specification documents (软件设计说明书) through multi-phase code analysis.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Context-Optimized Architecture │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Phase 1: Metadata → project-metadata.json │
|
||||
│ ↓ │
|
||||
│ Phase 2: 6 Parallel → sections/section-N.md (直接写MD) │
|
||||
│ Agents ↓ 返回简要JSON │
|
||||
│ ↓ │
|
||||
│ Phase 2.5: Consolidation → cross-module-summary.md │
|
||||
│ Agent ↓ 返回问题列表 │
|
||||
│ ↓ │
|
||||
│ Phase 4: Assembly → 合并MD + 跨模块总结 │
|
||||
│ ↓ │
|
||||
│ Phase 5: Refinement → 最终文档 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **Agent 直接输出 MD**: 避免 JSON → MD 转换的上下文开销
|
||||
2. **简要返回**: Agent 只返回路径+摘要,不返回完整内容
|
||||
3. **汇总 Agent**: 独立 Agent 负责跨模块问题检测
|
||||
4. **引用合并**: Phase 4 读取文件合并,不在上下文中传递
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Phase 1: Metadata Collection │
|
||||
│ → Read: phases/01-metadata-collection.md │
|
||||
│ → Collect: software name, version, category, scope │
|
||||
│ → Output: project-metadata.json │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 2: Deep Code Analysis (6 Parallel Agents) │
|
||||
│ → Read: phases/02-deep-analysis.md │
|
||||
│ → Reference: specs/cpcc-requirements.md │
|
||||
│ → Each Agent: 分析代码 → 直接写 sections/section-N.md │
|
||||
│ → Return: {"status", "output_file", "summary", "cross_notes"} │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 2.5: Consolidation (New!) │
|
||||
│ → Read: phases/02.5-consolidation.md │
|
||||
│ → Input: Agent 返回的简要信息 + cross_module_notes │
|
||||
│ → Analyze: 一致性/完整性/关联性/质量检查 │
|
||||
│ → Output: cross-module-summary.md │
|
||||
│ → Return: {"issues": {errors, warnings, info}, "stats"} │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 4: Document Assembly │
|
||||
│ → Read: phases/04-document-assembly.md │
|
||||
│ → Check: 如有 errors,提示用户处理 │
|
||||
│ → Merge: Section 1 + sections/*.md + 跨模块附录 │
|
||||
│ → Output: {软件名称}-软件设计说明书.md │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 5: Compliance Review & Refinement │
|
||||
│ → Read: phases/05-compliance-refinement.md │
|
||||
│ → Reference: specs/cpcc-requirements.md │
|
||||
│ → Loop: 发现问题 → 提问 → 修复 → 重新检查 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Document Sections (7 Required)
|
||||
|
||||
| Section | Title | Diagram | Agent |
|
||||
|---------|-------|---------|-------|
|
||||
| 1 | 软件概述 | - | Phase 4 生成 |
|
||||
| 2 | 系统架构图 | graph TD | architecture |
|
||||
| 3 | 功能模块设计 | flowchart TD | functions |
|
||||
| 4 | 核心算法与流程 | flowchart TD | algorithms |
|
||||
| 5 | 数据结构设计 | classDiagram | data_structures |
|
||||
| 6 | 接口设计 | sequenceDiagram | interfaces |
|
||||
| 7 | 异常处理设计 | flowchart TD | exceptions |
|
||||
|
||||
## Directory Setup
|
||||
|
||||
```javascript
|
||||
// 生成时间戳目录名
|
||||
const timestamp = new Date().toISOString().slice(0,19).replace(/[-:T]/g, '');
|
||||
const dir = `.workflow/.scratchpad/copyright-${timestamp}`;
|
||||
|
||||
// Windows (cmd)
|
||||
Bash(`mkdir "${dir}\\sections"`);
|
||||
Bash(`mkdir "${dir}\\iterations"`);
|
||||
|
||||
// Unix/macOS
|
||||
// Bash(`mkdir -p "${dir}/sections" "${dir}/iterations"`);
|
||||
```
|
||||
|
||||
## Output Structure
|
||||
|
||||
```
|
||||
.workflow/.scratchpad/copyright-{timestamp}/
|
||||
├── project-metadata.json # Phase 1
|
||||
├── sections/ # Phase 2 (Agent 直接写入)
|
||||
│ ├── section-2-architecture.md
|
||||
│ ├── section-3-functions.md
|
||||
│ ├── section-4-algorithms.md
|
||||
│ ├── section-5-data-structures.md
|
||||
│ ├── section-6-interfaces.md
|
||||
│ └── section-7-exceptions.md
|
||||
├── cross-module-summary.md # Phase 2.5
|
||||
├── iterations/ # Phase 5
|
||||
│ ├── v1.md
|
||||
│ └── v2.md
|
||||
└── {软件名称}-软件设计说明书.md # Final Output
|
||||
```
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [phases/01-metadata-collection.md](phases/01-metadata-collection.md) | Software info collection |
|
||||
| [phases/02-deep-analysis.md](phases/02-deep-analysis.md) | 6-agent parallel analysis |
|
||||
| [phases/02.5-consolidation.md](phases/02.5-consolidation.md) | Cross-module consolidation |
|
||||
| [phases/04-document-assembly.md](phases/04-document-assembly.md) | Document merge & assembly |
|
||||
| [phases/05-compliance-refinement.md](phases/05-compliance-refinement.md) | Iterative refinement loop |
|
||||
| [specs/cpcc-requirements.md](specs/cpcc-requirements.md) | CPCC compliance checklist |
|
||||
| [templates/agent-base.md](templates/agent-base.md) | Agent prompt templates |
|
||||
| [../_shared/mermaid-utils.md](../_shared/mermaid-utils.md) | Shared Mermaid utilities |
|
||||
@@ -0,0 +1,78 @@
|
||||
# Phase 1: Metadata Collection
|
||||
|
||||
Collect software metadata for document header and context.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Software Name & Version
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请输入软件名称(将显示在文档页眉):",
|
||||
header: "软件名称",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "自动检测", description: "从 package.json 或项目配置读取"},
|
||||
{label: "手动输入", description: "输入自定义名称"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Step 2: Software Category
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "软件属于哪种类型?",
|
||||
header: "软件类型",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "命令行工具 (CLI)", description: "重点描述命令、参数"},
|
||||
{label: "后端服务/API", description: "重点描述端点、协议"},
|
||||
{label: "SDK/库", description: "重点描述接口、集成"},
|
||||
{label: "数据处理系统", description: "重点描述数据流、转换"},
|
||||
{label: "自动化脚本", description: "重点描述工作流、触发器"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Step 3: Scope Definition
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "分析范围是什么?",
|
||||
header: "分析范围",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "整个项目", description: "分析全部源代码"},
|
||||
{label: "指定目录", description: "仅分析 src/ 或其他目录"},
|
||||
{label: "自定义路径", description: "手动指定路径"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Save metadata to `project-metadata.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"software_name": "智能数据分析系统",
|
||||
"version": "V1.0.0",
|
||||
"category": "后端服务/API",
|
||||
"scope_path": "src/",
|
||||
"tech_stack": {
|
||||
"language": "TypeScript",
|
||||
"runtime": "Node.js 18+",
|
||||
"framework": "Express.js",
|
||||
"dependencies": ["mongoose", "redis", "bull"]
|
||||
},
|
||||
"entry_points": ["src/index.ts", "src/cli.ts"],
|
||||
"main_modules": ["auth", "data", "api", "worker"]
|
||||
}
|
||||
```
|
||||
150
.claude/skills/copyright-docs/phases/01.5-project-exploration.md
Normal file
150
.claude/skills/copyright-docs/phases/01.5-project-exploration.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Phase 1.5: Project Exploration
|
||||
|
||||
基于元数据,启动并行探索 Agent 收集代码信息。
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Intelligent Angle Selection
|
||||
|
||||
```javascript
|
||||
// 根据软件类型选择探索角度
|
||||
const ANGLE_PRESETS = {
|
||||
'CLI': ['architecture', 'commands', 'algorithms', 'exceptions'],
|
||||
'API': ['architecture', 'endpoints', 'data-structures', 'interfaces'],
|
||||
'SDK': ['architecture', 'interfaces', 'data-structures', 'algorithms'],
|
||||
'DataProcessing': ['architecture', 'algorithms', 'data-structures', 'dataflow'],
|
||||
'Automation': ['architecture', 'algorithms', 'exceptions', 'dataflow']
|
||||
};
|
||||
|
||||
// 从 metadata.category 映射到预设
|
||||
function getCategoryKey(category) {
|
||||
if (category.includes('CLI') || category.includes('命令行')) return 'CLI';
|
||||
if (category.includes('API') || category.includes('后端')) return 'API';
|
||||
if (category.includes('SDK') || category.includes('库')) return 'SDK';
|
||||
if (category.includes('数据处理')) return 'DataProcessing';
|
||||
if (category.includes('自动化')) return 'Automation';
|
||||
return 'API'; // default
|
||||
}
|
||||
|
||||
const categoryKey = getCategoryKey(metadata.category);
|
||||
const selectedAngles = ANGLE_PRESETS[categoryKey];
|
||||
|
||||
console.log(`
|
||||
## Exploration Plan
|
||||
|
||||
Software: ${metadata.software_name}
|
||||
Category: ${metadata.category} → ${categoryKey}
|
||||
Selected Angles: ${selectedAngles.join(', ')}
|
||||
|
||||
Launching ${selectedAngles.length} parallel explorations...
|
||||
`);
|
||||
```
|
||||
|
||||
### Step 2: Launch Parallel Agents (Direct Output)
|
||||
|
||||
**⚠️ CRITICAL**: Agents write output files directly.
|
||||
|
||||
```javascript
|
||||
const explorationTasks = selectedAngles.map((angle, index) =>
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
description: `Explore: ${angle}`,
|
||||
prompt: `
|
||||
## Exploration Objective
|
||||
为 CPCC 软著申请文档执行 **${angle}** 探索。
|
||||
|
||||
## Assigned Context
|
||||
- **Exploration Angle**: ${angle}
|
||||
- **Software Name**: ${metadata.software_name}
|
||||
- **Scope Path**: ${metadata.scope_path}
|
||||
- **Category**: ${metadata.category}
|
||||
- **Exploration Index**: ${index + 1} of ${selectedAngles.length}
|
||||
- **Output File**: ${sessionFolder}/exploration-${angle}.json
|
||||
|
||||
## MANDATORY FIRST STEPS
|
||||
1. Run: ccw tool exec get_modules_by_depth '{}' (project structure)
|
||||
2. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files)
|
||||
3. Analyze from ${angle} perspective
|
||||
|
||||
## Exploration Strategy (${angle} focus)
|
||||
|
||||
**Step 1: Structural Scan**
|
||||
- 识别与 ${angle} 相关的模块和文件
|
||||
- 分析导入/导出关系
|
||||
|
||||
**Step 2: Pattern Recognition**
|
||||
- ${angle} 相关的设计模式
|
||||
- 代码组织方式
|
||||
|
||||
**Step 3: Write Output**
|
||||
- 输出 JSON 到指定路径
|
||||
|
||||
## Expected Output Schema
|
||||
|
||||
**File**: ${sessionFolder}/exploration-${angle}.json
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"angle": "${angle}",
|
||||
"findings": {
|
||||
"structure": [
|
||||
{ "component": "...", "type": "module|layer|service", "path": "...", "description": "..." }
|
||||
],
|
||||
"patterns": [
|
||||
{ "name": "...", "usage": "...", "files": ["path1", "path2"] }
|
||||
],
|
||||
"key_files": [
|
||||
{ "path": "src/file.ts", "relevance": 0.85, "rationale": "Core ${angle} logic" }
|
||||
]
|
||||
},
|
||||
"insights": [
|
||||
{ "observation": "...", "cpcc_section": "2|3|4|5|6|7", "recommendation": "..." }
|
||||
],
|
||||
"_metadata": {
|
||||
"exploration_angle": "${angle}",
|
||||
"exploration_index": ${index + 1},
|
||||
"software_name": "${metadata.software_name}",
|
||||
"timestamp": "ISO8601"
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Success Criteria
|
||||
- [ ] get_modules_by_depth 执行完成
|
||||
- [ ] 至少识别 3 个相关文件
|
||||
- [ ] patterns 包含具体代码示例
|
||||
- [ ] insights 关联到 CPCC 章节 (2-7)
|
||||
- [ ] JSON 输出到指定路径
|
||||
- [ ] Return: 2-3 句话总结 ${angle} 发现
|
||||
`
|
||||
})
|
||||
);
|
||||
|
||||
// Execute all exploration tasks in parallel
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Session folder structure after exploration:
|
||||
|
||||
```
|
||||
${sessionFolder}/
|
||||
├── exploration-architecture.json
|
||||
├── exploration-{angle2}.json
|
||||
├── exploration-{angle3}.json
|
||||
└── exploration-{angle4}.json
|
||||
```
|
||||
|
||||
## Downstream Usage (Phase 2 Analysis Input)
|
||||
|
||||
Phase 2 agents read exploration files as context:
|
||||
|
||||
```javascript
|
||||
// Discover exploration files by known angle pattern
|
||||
const explorationData = {};
|
||||
selectedAngles.forEach(angle => {
|
||||
const filePath = `${sessionFolder}/exploration-${angle}.json`;
|
||||
explorationData[angle] = JSON.parse(Read(filePath));
|
||||
});
|
||||
```
|
||||
664
.claude/skills/copyright-docs/phases/02-deep-analysis.md
Normal file
664
.claude/skills/copyright-docs/phases/02-deep-analysis.md
Normal file
@@ -0,0 +1,664 @@
|
||||
# Phase 2: Deep Code Analysis
|
||||
|
||||
6 个并行 Agent,各自直接写入 MD 章节文件。
|
||||
|
||||
> **模板参考**: [../templates/agent-base.md](../templates/agent-base.md)
|
||||
> **规范参考**: [../specs/cpcc-requirements.md](../specs/cpcc-requirements.md)
|
||||
|
||||
## Exploration → Agent 自动分配
|
||||
|
||||
根据 Phase 1.5 生成的 exploration 文件名自动分配对应的 analysis agent。
|
||||
|
||||
### 映射规则
|
||||
|
||||
```javascript
|
||||
// Exploration 角度 → Agent 映射(基于文件名识别,不读取内容)
|
||||
const EXPLORATION_TO_AGENT = {
|
||||
'architecture': 'architecture',
|
||||
'commands': 'functions', // CLI 命令 → 功能模块
|
||||
'endpoints': 'interfaces', // API 端点 → 接口设计
|
||||
'algorithms': 'algorithms',
|
||||
'data-structures': 'data_structures',
|
||||
'dataflow': 'data_structures', // 数据流 → 数据结构
|
||||
'interfaces': 'interfaces',
|
||||
'exceptions': 'exceptions'
|
||||
};
|
||||
|
||||
// 从文件名提取角度
|
||||
function extractAngle(filename) {
|
||||
// exploration-architecture.json → architecture
|
||||
const match = filename.match(/exploration-(.+)\.json$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
// 分配 agent
|
||||
function assignAgent(explorationFile) {
|
||||
const angle = extractAngle(path.basename(explorationFile));
|
||||
return EXPLORATION_TO_AGENT[angle] || null;
|
||||
}
|
||||
|
||||
// Agent 配置(用于 buildAgentPrompt)
|
||||
const AGENT_CONFIGS = {
|
||||
architecture: {
|
||||
role: '系统架构师,专注于分层设计和模块依赖',
|
||||
section: '2',
|
||||
output: 'section-2-architecture.md',
|
||||
focus: '分层结构、模块依赖、数据流向'
|
||||
},
|
||||
functions: {
|
||||
role: '功能分析师,专注于功能点识别和交互',
|
||||
section: '3',
|
||||
output: 'section-3-functions.md',
|
||||
focus: '功能点枚举、模块分组、入口文件、功能交互'
|
||||
},
|
||||
algorithms: {
|
||||
role: '算法工程师,专注于核心逻辑和复杂度分析',
|
||||
section: '4',
|
||||
output: 'section-4-algorithms.md',
|
||||
focus: '核心算法、流程步骤、复杂度、输入输出'
|
||||
},
|
||||
data_structures: {
|
||||
role: '数据建模师,专注于实体关系和类型定义',
|
||||
section: '5',
|
||||
output: 'section-5-data-structures.md',
|
||||
focus: '实体定义、属性类型、关系映射、枚举'
|
||||
},
|
||||
interfaces: {
|
||||
role: 'API设计师,专注于接口契约和协议',
|
||||
section: '6',
|
||||
output: 'section-6-interfaces.md',
|
||||
focus: 'API端点、参数校验、响应格式、时序'
|
||||
},
|
||||
exceptions: {
|
||||
role: '可靠性工程师,专注于异常处理和恢复策略',
|
||||
section: '7',
|
||||
output: 'section-7-exceptions.md',
|
||||
focus: '异常类型、错误码、处理模式、恢复策略'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 自动发现与分配流程
|
||||
|
||||
```javascript
|
||||
// 1. 发现所有 exploration 文件(仅看文件名)
|
||||
const explorationFiles = bash(`find ${sessionFolder} -name "exploration-*.json" -type f`)
|
||||
.split('\n')
|
||||
.filter(f => f.trim());
|
||||
|
||||
// 2. 按文件名自动分配 agent
|
||||
const agentAssignments = explorationFiles.map(file => {
|
||||
const angle = extractAngle(path.basename(file));
|
||||
const agentName = EXPLORATION_TO_AGENT[angle];
|
||||
return {
|
||||
exploration_file: file,
|
||||
angle: angle,
|
||||
agent: agentName,
|
||||
output_file: AGENT_CONFIGS[agentName]?.output
|
||||
};
|
||||
}).filter(a => a.agent);
|
||||
|
||||
// 3. 补充未被 exploration 覆盖的必需 agent(分配相关 exploration)
|
||||
const coveredAgents = new Set(agentAssignments.map(a => a.agent));
|
||||
const requiredAgents = ['architecture', 'functions', 'algorithms', 'data_structures', 'interfaces', 'exceptions'];
|
||||
const missingAgents = requiredAgents.filter(a => !coveredAgents.has(a));
|
||||
|
||||
// 相关性映射:为缺失 agent 分配最相关的 exploration
|
||||
const RELATED_EXPLORATIONS = {
|
||||
architecture: ['architecture', 'dataflow', 'interfaces'],
|
||||
functions: ['commands', 'endpoints', 'architecture'],
|
||||
algorithms: ['algorithms', 'dataflow', 'architecture'],
|
||||
data_structures: ['data-structures', 'dataflow', 'architecture'],
|
||||
interfaces: ['interfaces', 'endpoints', 'architecture'],
|
||||
exceptions: ['exceptions', 'algorithms', 'architecture']
|
||||
};
|
||||
|
||||
function findRelatedExploration(agent, availableFiles) {
|
||||
const preferences = RELATED_EXPLORATIONS[agent] || ['architecture'];
|
||||
for (const pref of preferences) {
|
||||
const match = availableFiles.find(f => f.includes(`exploration-${pref}.json`));
|
||||
if (match) return { file: match, angle: pref, isRelated: true };
|
||||
}
|
||||
// 最后兜底:任意 exploration 都比没有强
|
||||
return availableFiles.length > 0
|
||||
? { file: availableFiles[0], angle: extractAngle(path.basename(availableFiles[0])), isRelated: true }
|
||||
: { file: null, angle: null, isRelated: false };
|
||||
}
|
||||
|
||||
missingAgents.forEach(agent => {
|
||||
const related = findRelatedExploration(agent, explorationFiles);
|
||||
agentAssignments.push({
|
||||
exploration_file: related.file,
|
||||
angle: related.angle,
|
||||
agent: agent,
|
||||
output_file: AGENT_CONFIGS[agent].output,
|
||||
is_related: related.isRelated // 标记为相关而非直接匹配
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`
|
||||
## Agent Auto-Assignment
|
||||
|
||||
Found ${explorationFiles.length} exploration files:
|
||||
${agentAssignments.map(a => {
|
||||
if (!a.exploration_file) return `- ${a.agent} agent (no exploration)`;
|
||||
if (a.is_related) return `- ${a.agent} agent ← ${a.angle} (related)`;
|
||||
return `- ${a.agent} agent ← ${a.angle} (direct)`;
|
||||
}).join('\n')}
|
||||
`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent 执行前置条件
|
||||
|
||||
**每个 Agent 接收 exploration 文件路径,自行读取内容**:
|
||||
|
||||
```javascript
|
||||
// Agent prompt 中包含文件路径
|
||||
// Agent 启动后的操作顺序:
|
||||
// 1. Read exploration 文件(如有)
|
||||
// 2. Read CPCC 规范文件
|
||||
// 3. 执行分析任务
|
||||
```
|
||||
|
||||
规范文件路径(相对于 skill 根目录):
|
||||
- `specs/cpcc-requirements.md` - CPCC 软著申请规范要求
|
||||
|
||||
---
|
||||
|
||||
## Agent 配置
|
||||
|
||||
| Agent | 输出文件 | 章节 |
|
||||
|-------|----------|------|
|
||||
| architecture | section-2-architecture.md | 系统架构图 |
|
||||
| functions | section-3-functions.md | 功能模块设计 |
|
||||
| algorithms | section-4-algorithms.md | 核心算法与流程 |
|
||||
| data_structures | section-5-data-structures.md | 数据结构设计 |
|
||||
| interfaces | section-6-interfaces.md | 接口设计 |
|
||||
| exceptions | section-7-exceptions.md | 异常处理设计 |
|
||||
|
||||
## CPCC 规范要点 (所有 Agent 共用)
|
||||
|
||||
```
|
||||
[CPCC_SPEC]
|
||||
1. 内容基于代码分析,无臆测或未来计划
|
||||
2. 图表编号格式: 图N-M (如图2-1, 图3-1)
|
||||
3. 每个子章节内容不少于100字
|
||||
4. Mermaid 语法必须正确可渲染
|
||||
5. 包含具体文件路径引用
|
||||
6. 中文输出,技术术语可用英文
|
||||
```
|
||||
|
||||
## 执行流程
|
||||
|
||||
```javascript
|
||||
// 1. 发现 exploration 文件并自动分配 agent
|
||||
const explorationFiles = bash(`find ${sessionFolder} -name "exploration-*.json" -type f`)
|
||||
.split('\n')
|
||||
.filter(f => f.trim());
|
||||
|
||||
const agentAssignments = explorationFiles.map(file => {
|
||||
const angle = extractAngle(path.basename(file));
|
||||
const agentName = EXPLORATION_TO_AGENT[angle];
|
||||
return { exploration_file: file, angle, agent: agentName };
|
||||
}).filter(a => a.agent);
|
||||
|
||||
// 补充必需 agent
|
||||
const coveredAgents = new Set(agentAssignments.map(a => a.agent));
|
||||
const requiredAgents = ['architecture', 'functions', 'algorithms', 'data_structures', 'interfaces', 'exceptions'];
|
||||
requiredAgents.filter(a => !coveredAgents.has(a)).forEach(agent => {
|
||||
agentAssignments.push({ exploration_file: null, angle: null, agent });
|
||||
});
|
||||
|
||||
// 2. 准备目录
|
||||
Bash(`mkdir -p ${outputDir}/sections`);
|
||||
|
||||
// 3. 并行启动所有 Agent(传递 exploration 文件路径)
|
||||
const results = await Promise.all(
|
||||
agentAssignments.map(assignment =>
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
description: `Analyze: ${assignment.agent}`,
|
||||
prompt: buildAgentPrompt(assignment, metadata, outputDir)
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// 4. 收集返回信息
|
||||
const summaries = results.map(r => JSON.parse(r));
|
||||
|
||||
// 5. 传递给 Phase 2.5
|
||||
return { summaries, cross_notes: summaries.flatMap(s => s.cross_module_notes) };
|
||||
```
|
||||
|
||||
### Agent Prompt 构建
|
||||
|
||||
```javascript
|
||||
function buildAgentPrompt(assignment, metadata, outputDir) {
|
||||
const config = AGENT_CONFIGS[assignment.agent];
|
||||
let contextSection = '';
|
||||
|
||||
if (assignment.exploration_file) {
|
||||
const matchType = assignment.is_related ? '相关' : '直接匹配';
|
||||
contextSection = `[CONTEXT]
|
||||
**Exploration 文件**: ${assignment.exploration_file}
|
||||
**匹配类型**: ${matchType}
|
||||
首先读取此文件获取 ${assignment.angle} 探索结果作为分析上下文。
|
||||
${assignment.is_related ? `注意:这是相关探索结果(非直接匹配),请提取与 ${config.focus} 相关的信息。` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
${contextSection}
|
||||
[SPEC]
|
||||
读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
|
||||
[ROLE] ${config.role}
|
||||
|
||||
[TASK]
|
||||
分析 ${metadata.scope_path},生成 Section ${config.section}。
|
||||
输出: ${outputDir}/sections/${config.output}
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图${config.section}-1, 图${config.section}-2...
|
||||
- 每个子章节 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[FOCUS]
|
||||
${config.focus}
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"${config.output}","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent 提示词
|
||||
|
||||
### Architecture
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] 系统架构师,专注于分层设计和模块依赖。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 2: 系统架构图。
|
||||
输出: ${outDir}/sections/section-2-architecture.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图2-1, 图2-2...
|
||||
- 每个子章节 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 2. 系统架构图
|
||||
|
||||
本章节展示${meta.software_name}的系统架构设计。
|
||||
|
||||
\`\`\`mermaid
|
||||
graph TD
|
||||
subgraph Layer1["层名"]
|
||||
Comp1[组件1]
|
||||
end
|
||||
Comp1 --> Comp2
|
||||
\`\`\`
|
||||
|
||||
**图2-1 系统架构图**
|
||||
|
||||
### 2.1 分层说明
|
||||
| 层级 | 组件 | 职责 |
|
||||
|------|------|------|
|
||||
|
||||
### 2.2 模块依赖
|
||||
| 模块 | 依赖 | 说明 |
|
||||
|------|------|------|
|
||||
|
||||
[FOCUS]
|
||||
1. 分层: 识别代码层次 (Controller/Service/Repository 或其他)
|
||||
2. 模块: 核心模块及职责边界
|
||||
3. 依赖: 模块间依赖方向
|
||||
4. 数据流: 请求/数据的流动路径
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-2-architecture.md","summary":"<50字摘要>","cross_module_notes":["跨模块发现"],"stats":{"diagrams":1,"subsections":2}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] 功能分析师,专注于功能点识别和交互。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 3: 功能模块设计。
|
||||
输出: ${outDir}/sections/section-3-functions.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图3-1, 图3-2...
|
||||
- 每个子章节 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 3. 功能模块设计
|
||||
|
||||
本章节展示${meta.software_name}的功能模块结构。
|
||||
|
||||
\`\`\`mermaid
|
||||
flowchart TD
|
||||
ROOT["${meta.software_name}"]
|
||||
subgraph Group1["模块组1"]
|
||||
F1["功能1"]
|
||||
end
|
||||
ROOT --> Group1
|
||||
\`\`\`
|
||||
|
||||
**图3-1 功能模块结构图**
|
||||
|
||||
### 3.1 功能清单
|
||||
| ID | 功能名称 | 模块 | 入口文件 | 说明 |
|
||||
|----|----------|------|----------|------|
|
||||
|
||||
### 3.2 功能交互
|
||||
| 调用方 | 被调用方 | 触发条件 |
|
||||
|--------|----------|----------|
|
||||
|
||||
[FOCUS]
|
||||
1. 功能点: 枚举所有用户可见功能
|
||||
2. 模块分组: 按业务域分组
|
||||
3. 入口: 每个功能的代码入口 \`src/path/file.ts\`
|
||||
4. 交互: 功能间的调用关系
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-3-functions.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Algorithms
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] 算法工程师,专注于核心逻辑和复杂度分析。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 4: 核心算法与流程。
|
||||
输出: ${outDir}/sections/section-4-algorithms.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图4-1, 图4-2... (每个算法一个流程图)
|
||||
- 每个算法说明 ≥100字
|
||||
- 包含文件路径和行号引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 4. 核心算法与流程
|
||||
|
||||
本章节展示${meta.software_name}的核心算法设计。
|
||||
|
||||
### 4.1 {算法名称}
|
||||
|
||||
**说明**: {描述,≥100字}
|
||||
**位置**: \`src/path/file.ts:line\`
|
||||
|
||||
**输入**: param1 (type) - 说明
|
||||
**输出**: result (type) - 说明
|
||||
|
||||
\`\`\`mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> Input[/输入/]
|
||||
Input --> Check{判断}
|
||||
Check -->|是| P1[步骤1]
|
||||
Check -->|否| P2[步骤2]
|
||||
P1 --> End([结束])
|
||||
P2 --> End
|
||||
\`\`\`
|
||||
|
||||
**图4-1 {算法名称}流程图**
|
||||
|
||||
### 4.N 复杂度分析
|
||||
| 算法 | 时间 | 空间 | 文件 |
|
||||
|------|------|------|------|
|
||||
|
||||
[FOCUS]
|
||||
1. 核心算法: 业务逻辑的关键算法 (>10行或含分支循环)
|
||||
2. 流程步骤: 分支/循环/条件逻辑
|
||||
3. 复杂度: 时间/空间复杂度估算
|
||||
4. 输入输出: 参数类型和返回值
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-4-algorithms.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Data Structures
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] 数据建模师,专注于实体关系和类型定义。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 5: 数据结构设计。
|
||||
输出: ${outDir}/sections/section-5-data-structures.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图5-1 (数据结构类图)
|
||||
- 每个子章节 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 5. 数据结构设计
|
||||
|
||||
本章节展示${meta.software_name}的核心数据结构。
|
||||
|
||||
\`\`\`mermaid
|
||||
classDiagram
|
||||
class Entity1 {
|
||||
+type field1
|
||||
+method1()
|
||||
}
|
||||
Entity1 "1" --> "*" Entity2 : 关系
|
||||
\`\`\`
|
||||
|
||||
**图5-1 数据结构类图**
|
||||
|
||||
### 5.1 实体说明
|
||||
| 实体 | 类型 | 文件 | 说明 |
|
||||
|------|------|------|------|
|
||||
|
||||
### 5.2 关系说明
|
||||
| 源 | 目标 | 类型 | 基数 |
|
||||
|----|------|------|------|
|
||||
|
||||
[FOCUS]
|
||||
1. 实体: class/interface/type 定义
|
||||
2. 属性: 字段类型和可见性 (+public/-private/#protected)
|
||||
3. 关系: 继承(--|>)/组合(*--)/关联(-->)
|
||||
4. 枚举: enum 类型及其值
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-5-data-structures.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Interfaces
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] API设计师,专注于接口契约和协议。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 6: 接口设计。
|
||||
输出: ${outDir}/sections/section-6-interfaces.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图6-1, 图6-2... (每个核心接口一个时序图)
|
||||
- 每个接口详情 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 6. 接口设计
|
||||
|
||||
本章节展示${meta.software_name}的接口设计。
|
||||
|
||||
\`\`\`mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant A as API
|
||||
participant S as Service
|
||||
C->>A: POST /api/xxx
|
||||
A->>S: method()
|
||||
S-->>A: result
|
||||
A-->>C: 200 OK
|
||||
\`\`\`
|
||||
|
||||
**图6-1 {接口名}时序图**
|
||||
|
||||
### 6.1 接口清单
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
|
||||
### 6.2 接口详情
|
||||
|
||||
#### METHOD /path
|
||||
**请求**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
|
||||
**响应**:
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
|
||||
[FOCUS]
|
||||
1. API端点: 路径/方法/说明
|
||||
2. 参数: 请求参数类型和校验规则
|
||||
3. 响应: 响应格式、状态码、错误码
|
||||
4. 时序: 典型调用流程 (选2-3个核心接口)
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-6-interfaces.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Exceptions
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
[ROLE] 可靠性工程师,专注于异常处理和恢复策略。
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 Section 7: 异常处理设计。
|
||||
输出: ${outDir}/sections/section-7-exceptions.md
|
||||
|
||||
[CPCC_SPEC]
|
||||
- 内容基于代码分析,无臆测
|
||||
- 图表编号: 图7-1 (异常处理流程图)
|
||||
- 每个子章节 ≥100字
|
||||
- 包含文件路径引用
|
||||
|
||||
[TEMPLATE]
|
||||
## 7. 异常处理设计
|
||||
|
||||
本章节展示${meta.software_name}的异常处理机制。
|
||||
|
||||
\`\`\`mermaid
|
||||
flowchart TD
|
||||
Req[请求] --> Try{Try-Catch}
|
||||
Try -->|正常| Process[处理]
|
||||
Try -->|异常| ErrType{类型}
|
||||
ErrType -->|E1| H1[处理1]
|
||||
ErrType -->|E2| H2[处理2]
|
||||
H1 --> Log[日志]
|
||||
H2 --> Log
|
||||
Process --> Resp[响应]
|
||||
\`\`\`
|
||||
|
||||
**图7-1 异常处理流程图**
|
||||
|
||||
### 7.1 异常类型
|
||||
| 异常类 | 错误码 | HTTP状态 | 说明 |
|
||||
|--------|--------|----------|------|
|
||||
|
||||
### 7.2 恢复策略
|
||||
| 场景 | 策略 | 说明 |
|
||||
|------|------|------|
|
||||
|
||||
[FOCUS]
|
||||
1. 异常类型: 自定义异常类及继承关系
|
||||
2. 错误码: 错误码定义和分类
|
||||
3. 处理模式: try-catch/中间件/装饰器
|
||||
4. 恢复策略: 重试/降级/熔断/告警
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-7-exceptions.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
各 Agent 写入 `sections/section-N-xxx.md`,返回简要 JSON 供 Phase 2.5 汇总。
|
||||
192
.claude/skills/copyright-docs/phases/02.5-consolidation.md
Normal file
192
.claude/skills/copyright-docs/phases/02.5-consolidation.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Phase 2.5: Consolidation Agent
|
||||
|
||||
汇总所有分析 Agent 的产出,生成设计综述,为 Phase 4 索引文档提供内容。
|
||||
|
||||
> **规范参考**: [../specs/cpcc-requirements.md](../specs/cpcc-requirements.md)
|
||||
|
||||
## 核心职责
|
||||
|
||||
1. **设计综述**:生成 synthesis(软件整体设计思路)
|
||||
2. **章节摘要**:生成 section_summaries(导航表格内容)
|
||||
3. **跨模块分析**:识别问题和关联
|
||||
4. **质量检查**:验证 CPCC 合规性
|
||||
|
||||
## 输入
|
||||
|
||||
```typescript
|
||||
interface ConsolidationInput {
|
||||
output_dir: string;
|
||||
agent_summaries: AgentReturn[];
|
||||
cross_module_notes: string[];
|
||||
metadata: ProjectMetadata;
|
||||
}
|
||||
```
|
||||
|
||||
## 执行
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
## 规范前置
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/cpcc-requirements.md
|
||||
严格遵循 CPCC 软著申请规范要求。
|
||||
|
||||
## 任务
|
||||
作为汇总 Agent,读取所有章节文件,生成设计综述和跨模块分析报告。
|
||||
|
||||
## 输入
|
||||
- 章节文件: ${outputDir}/sections/section-*.md
|
||||
- Agent 摘要: ${JSON.stringify(agent_summaries)}
|
||||
- 跨模块备注: ${JSON.stringify(cross_module_notes)}
|
||||
- 软件信息: ${JSON.stringify(metadata)}
|
||||
|
||||
## 核心产出
|
||||
|
||||
### 1. 设计综述 (synthesis)
|
||||
用 2-3 段落描述软件整体设计思路:
|
||||
- 第一段:软件定位与核心设计理念
|
||||
- 第二段:模块划分与协作机制
|
||||
- 第三段:技术选型与设计特点
|
||||
|
||||
### 2. 章节摘要 (section_summaries)
|
||||
为每个章节提取一句话说明,用于导航表格:
|
||||
|
||||
| 章节 | 文件 | 一句话说明 |
|
||||
|------|------|------------|
|
||||
| 2. 系统架构设计 | section-2-architecture.md | ... |
|
||||
| 3. 功能模块设计 | section-3-functions.md | ... |
|
||||
| 4. 核心算法与流程 | section-4-algorithms.md | ... |
|
||||
| 5. 数据结构设计 | section-5-data-structures.md | ... |
|
||||
| 6. 接口设计 | section-6-interfaces.md | ... |
|
||||
| 7. 异常处理设计 | section-7-exceptions.md | ... |
|
||||
|
||||
### 3. 跨模块分析
|
||||
- 一致性:术语、命名规范
|
||||
- 完整性:功能-接口对应、异常覆盖
|
||||
- 关联性:模块依赖、数据流向
|
||||
|
||||
## 输出文件
|
||||
|
||||
写入: ${outputDir}/cross-module-summary.md
|
||||
|
||||
### 文件格式
|
||||
|
||||
\`\`\`markdown
|
||||
# 跨模块分析报告
|
||||
|
||||
## 设计综述
|
||||
|
||||
[2-3 段落的软件设计思路描述]
|
||||
|
||||
## 章节摘要
|
||||
|
||||
| 章节 | 文件 | 说明 |
|
||||
|------|------|------|
|
||||
| 2. 系统架构设计 | section-2-architecture.md | 一句话说明 |
|
||||
| ... | ... | ... |
|
||||
|
||||
## 文档统计
|
||||
|
||||
| 章节 | 图表数 | 字数 |
|
||||
|------|--------|------|
|
||||
| ... | ... | ... |
|
||||
|
||||
## 发现的问题
|
||||
|
||||
### 严重问题 (必须修复)
|
||||
|
||||
| ID | 类型 | 位置 | 描述 | 建议 |
|
||||
|----|------|------|------|------|
|
||||
| E001 | ... | ... | ... | ... |
|
||||
|
||||
### 警告 (建议修复)
|
||||
|
||||
| ID | 类型 | 位置 | 描述 | 建议 |
|
||||
|----|------|------|------|------|
|
||||
| W001 | ... | ... | ... | ... |
|
||||
|
||||
### 提示 (可选修复)
|
||||
|
||||
| ID | 类型 | 位置 | 描述 |
|
||||
|----|------|------|------|
|
||||
| I001 | ... | ... | ... |
|
||||
|
||||
## 跨模块关联图
|
||||
|
||||
\`\`\`mermaid
|
||||
graph LR
|
||||
S2[架构] --> S3[功能]
|
||||
S3 --> S4[算法]
|
||||
S3 --> S6[接口]
|
||||
S5[数据结构] --> S6
|
||||
S6 --> S7[异常]
|
||||
\`\`\`
|
||||
|
||||
## 修复建议优先级
|
||||
|
||||
[按优先级排序的建议,段落式描述]
|
||||
\`\`\`
|
||||
|
||||
## 返回格式 (JSON)
|
||||
|
||||
{
|
||||
"status": "completed",
|
||||
"output_file": "cross-module-summary.md",
|
||||
|
||||
// Phase 4 索引文档所需
|
||||
"synthesis": "2-3 段落的设计综述文本",
|
||||
"section_summaries": [
|
||||
{"file": "section-2-architecture.md", "title": "2. 系统架构设计", "summary": "一句话说明"},
|
||||
{"file": "section-3-functions.md", "title": "3. 功能模块设计", "summary": "一句话说明"},
|
||||
{"file": "section-4-algorithms.md", "title": "4. 核心算法与流程", "summary": "一句话说明"},
|
||||
{"file": "section-5-data-structures.md", "title": "5. 数据结构设计", "summary": "一句话说明"},
|
||||
{"file": "section-6-interfaces.md", "title": "6. 接口设计", "summary": "一句话说明"},
|
||||
{"file": "section-7-exceptions.md", "title": "7. 异常处理设计", "summary": "一句话说明"}
|
||||
],
|
||||
|
||||
// 质量信息
|
||||
"stats": {
|
||||
"total_sections": 6,
|
||||
"total_diagrams": 8,
|
||||
"total_words": 3500
|
||||
},
|
||||
"issues": {
|
||||
"errors": [...],
|
||||
"warnings": [...],
|
||||
"info": [...]
|
||||
},
|
||||
"cross_refs": {
|
||||
"found": 12,
|
||||
"missing": 3
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
## 问题分类
|
||||
|
||||
| 严重级别 | 前缀 | 含义 | 处理方式 |
|
||||
|----------|------|------|----------|
|
||||
| Error | E | 阻塞合规检查 | 必须修复 |
|
||||
| Warning | W | 影响文档质量 | 建议修复 |
|
||||
| Info | I | 可改进项 | 可选修复 |
|
||||
|
||||
## 问题类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| missing | 缺失内容(功能-接口对应、异常覆盖)|
|
||||
| inconsistency | 不一致(术语、命名、编号)|
|
||||
| circular | 循环依赖 |
|
||||
| orphan | 孤立内容(未被引用)|
|
||||
| syntax | Mermaid 语法错误 |
|
||||
| enhancement | 增强建议 |
|
||||
|
||||
## Output
|
||||
|
||||
- **文件**: `cross-module-summary.md`(完整汇总报告)
|
||||
- **返回**: JSON 包含 Phase 4 所需的 synthesis 和 section_summaries
|
||||
261
.claude/skills/copyright-docs/phases/04-document-assembly.md
Normal file
261
.claude/skills/copyright-docs/phases/04-document-assembly.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# Phase 4: Document Assembly
|
||||
|
||||
生成索引式文档,通过 markdown 链接引用章节文件。
|
||||
|
||||
> **规范参考**: [../specs/cpcc-requirements.md](../specs/cpcc-requirements.md)
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **引用而非嵌入**:主文档通过链接引用章节,不复制内容
|
||||
2. **索引 + 综述**:主文档提供导航和软件概述
|
||||
3. **CPCC 合规**:保持章节编号符合软著申请要求
|
||||
4. **独立可读**:各章节文件可单独阅读
|
||||
|
||||
## 输入
|
||||
|
||||
```typescript
|
||||
interface AssemblyInput {
|
||||
output_dir: string;
|
||||
metadata: ProjectMetadata;
|
||||
consolidation: {
|
||||
synthesis: string; // 跨章节综合分析
|
||||
section_summaries: Array<{
|
||||
file: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
}>;
|
||||
issues: { errors: Issue[], warnings: Issue[], info: Issue[] };
|
||||
stats: { total_sections: number, total_diagrams: number };
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 执行流程
|
||||
|
||||
```javascript
|
||||
// 1. 检查是否有阻塞性问题
|
||||
if (consolidation.issues.errors.length > 0) {
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: `发现 ${consolidation.issues.errors.length} 个严重问题,如何处理?`,
|
||||
header: "阻塞问题",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "查看并修复", description: "显示问题列表,手动修复后重试"},
|
||||
{label: "忽略继续", description: "跳过问题检查,继续装配"},
|
||||
{label: "终止", description: "停止文档生成"}
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (response === "查看并修复") {
|
||||
return { action: "fix_required", errors: consolidation.issues.errors };
|
||||
}
|
||||
if (response === "终止") {
|
||||
return { action: "abort" };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 生成索引式文档(不读取章节内容)
|
||||
const doc = generateIndexDocument(metadata, consolidation);
|
||||
|
||||
// 3. 写入最终文件
|
||||
Write(`${outputDir}/${metadata.software_name}-软件设计说明书.md`, doc);
|
||||
```
|
||||
|
||||
## 文档模板
|
||||
|
||||
```markdown
|
||||
<!-- 页眉:{软件名称} - 版本号:{版本号} -->
|
||||
|
||||
# {软件名称} 软件设计说明书
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 软件名称 | {software_name} |
|
||||
| 版本号 | {version} |
|
||||
| 生成日期 | {date} |
|
||||
|
||||
---
|
||||
|
||||
## 1. 软件概述
|
||||
|
||||
### 1.1 软件背景与用途
|
||||
|
||||
[从 metadata 生成的软件背景描述]
|
||||
|
||||
### 1.2 开发目标与特点
|
||||
|
||||
[从 metadata 生成的目标和特点]
|
||||
|
||||
### 1.3 运行环境与技术架构
|
||||
|
||||
[从 metadata.tech_stack 生成]
|
||||
|
||||
---
|
||||
|
||||
## 文档导航
|
||||
|
||||
{consolidation.synthesis - 软件整体设计思路综述}
|
||||
|
||||
| 章节 | 说明 | 详情 |
|
||||
|------|------|------|
|
||||
| 2. 系统架构设计 | {summary} | [查看](./sections/section-2-architecture.md) |
|
||||
| 3. 功能模块设计 | {summary} | [查看](./sections/section-3-functions.md) |
|
||||
| 4. 核心算法与流程 | {summary} | [查看](./sections/section-4-algorithms.md) |
|
||||
| 5. 数据结构设计 | {summary} | [查看](./sections/section-5-data-structures.md) |
|
||||
| 6. 接口设计 | {summary} | [查看](./sections/section-6-interfaces.md) |
|
||||
| 7. 异常处理设计 | {summary} | [查看](./sections/section-7-exceptions.md) |
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
- [跨模块分析报告](./cross-module-summary.md)
|
||||
- [章节文件目录](./sections/)
|
||||
|
||||
---
|
||||
|
||||
<!-- 页脚:生成时间 {timestamp} -->
|
||||
```
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generateIndexDocument(metadata, consolidation) {
|
||||
const date = new Date().toLocaleDateString('zh-CN');
|
||||
|
||||
// 章节导航表格
|
||||
const sectionTable = consolidation.section_summaries
|
||||
.map(s => `| ${s.title} | ${s.summary} | [查看](./sections/${s.file}) |`)
|
||||
.join('\n');
|
||||
|
||||
return `<!-- 页眉:${metadata.software_name} - 版本号:${metadata.version} -->
|
||||
|
||||
# ${metadata.software_name} 软件设计说明书
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 软件名称 | ${metadata.software_name} |
|
||||
| 版本号 | ${metadata.version} |
|
||||
| 生成日期 | ${date} |
|
||||
|
||||
---
|
||||
|
||||
## 1. 软件概述
|
||||
|
||||
### 1.1 软件背景与用途
|
||||
|
||||
${generateBackground(metadata)}
|
||||
|
||||
### 1.2 开发目标与特点
|
||||
|
||||
${generateObjectives(metadata)}
|
||||
|
||||
### 1.3 运行环境与技术架构
|
||||
|
||||
${generateTechStack(metadata)}
|
||||
|
||||
---
|
||||
|
||||
## 设计综述
|
||||
|
||||
${consolidation.synthesis}
|
||||
|
||||
---
|
||||
|
||||
## 文档导航
|
||||
|
||||
| 章节 | 说明 | 详情 |
|
||||
|------|------|------|
|
||||
${sectionTable}
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
- [跨模块分析报告](./cross-module-summary.md)
|
||||
- [章节文件目录](./sections/)
|
||||
|
||||
---
|
||||
|
||||
<!-- 页脚:生成时间 ${new Date().toISOString()} -->
|
||||
`;
|
||||
}
|
||||
|
||||
function generateBackground(metadata) {
|
||||
const categoryDescriptions = {
|
||||
"命令行工具 (CLI)": "提供命令行界面,用户通过终端命令与系统交互",
|
||||
"后端服务/API": "提供 RESTful/GraphQL API 接口,支持前端或其他服务调用",
|
||||
"SDK/库": "提供可复用的代码库,供其他项目集成使用",
|
||||
"数据处理系统": "处理数据导入、转换、分析和导出",
|
||||
"自动化脚本": "自动执行重复性任务,提高工作效率"
|
||||
};
|
||||
|
||||
return `${metadata.software_name}是一款${metadata.category}软件。${categoryDescriptions[metadata.category] || ''}
|
||||
|
||||
本软件基于${metadata.tech_stack.language}语言开发,运行于${metadata.tech_stack.runtime}环境,采用${metadata.tech_stack.framework || '原生'}框架实现核心功能。`;
|
||||
}
|
||||
|
||||
function generateObjectives(metadata) {
|
||||
return `本软件旨在${metadata.purpose || '解决特定领域的技术问题'}。
|
||||
|
||||
主要技术特点包括${metadata.tech_stack.framework ? `采用 ${metadata.tech_stack.framework} 框架` : '模块化设计'},具备良好的可扩展性和可维护性。`;
|
||||
}
|
||||
|
||||
function generateTechStack(metadata) {
|
||||
return `**运行环境**
|
||||
|
||||
- 操作系统:${metadata.os || 'Windows/Linux/macOS'}
|
||||
- 运行时:${metadata.tech_stack.runtime}
|
||||
- 依赖环境:${metadata.tech_stack.dependencies?.join(', ') || '无特殊依赖'}
|
||||
|
||||
**技术架构**
|
||||
|
||||
- 架构模式:${metadata.architecture_pattern || '分层架构'}
|
||||
- 核心框架:${metadata.tech_stack.framework || '原生实现'}
|
||||
- 主要模块:详见第2章系统架构设计`;
|
||||
}
|
||||
```
|
||||
|
||||
## 输出结构
|
||||
|
||||
```
|
||||
.workflow/.scratchpad/copyright-{timestamp}/
|
||||
├── sections/ # 独立章节(Phase 2 产出)
|
||||
│ ├── section-2-architecture.md
|
||||
│ ├── section-3-functions.md
|
||||
│ └── ...
|
||||
├── cross-module-summary.md # 跨模块报告(Phase 2.5 产出)
|
||||
└── {软件名称}-软件设计说明书.md # 索引文档(本阶段产出)
|
||||
```
|
||||
|
||||
## 与 Phase 2.5 的协作
|
||||
|
||||
Phase 2.5 consolidation agent 需要提供:
|
||||
|
||||
```typescript
|
||||
interface ConsolidationOutput {
|
||||
synthesis: string; // 设计思路综述(2-3 段落)
|
||||
section_summaries: Array<{
|
||||
file: string; // 文件名
|
||||
title: string; // 章节标题(如"2. 系统架构设计")
|
||||
summary: string; // 一句话说明
|
||||
}>;
|
||||
issues: {...};
|
||||
stats: {...};
|
||||
}
|
||||
```
|
||||
|
||||
## 关键变更
|
||||
|
||||
| 原设计 | 新设计 |
|
||||
|--------|--------|
|
||||
| 读取章节内容并拼接 | 链接引用,不读取内容 |
|
||||
| 嵌入完整章节 | 仅提供导航索引 |
|
||||
| 重复生成统计 | 引用 cross-module-summary.md |
|
||||
| 大文件 | 精简索引文档 |
|
||||
192
.claude/skills/copyright-docs/phases/05-compliance-refinement.md
Normal file
192
.claude/skills/copyright-docs/phases/05-compliance-refinement.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Phase 5: Compliance Review & Iterative Refinement
|
||||
|
||||
Discovery-driven refinement loop until CPCC compliance is met.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Extract Compliance Issues
|
||||
|
||||
```javascript
|
||||
function extractComplianceIssues(validationResult, deepAnalysis) {
|
||||
return {
|
||||
// Missing or incomplete sections
|
||||
missingSections: validationResult.details
|
||||
.filter(d => !d.pass)
|
||||
.map(d => ({
|
||||
section: d.name,
|
||||
severity: 'critical',
|
||||
suggestion: `需要补充 ${d.name} 相关内容`
|
||||
})),
|
||||
|
||||
// Features with weak descriptions (< 50 chars)
|
||||
weakDescriptions: (deepAnalysis.functions?.feature_list || [])
|
||||
.filter(f => !f.description || f.description.length < 50)
|
||||
.map(f => ({
|
||||
feature: f.name,
|
||||
current: f.description || '(无描述)',
|
||||
severity: 'warning'
|
||||
})),
|
||||
|
||||
// Complex algorithms without detailed flowcharts
|
||||
complexAlgorithms: (deepAnalysis.algorithms?.algorithms || [])
|
||||
.filter(a => (a.complexity || 0) > 10 && (a.steps?.length || 0) < 5)
|
||||
.map(a => ({
|
||||
algorithm: a.name,
|
||||
complexity: a.complexity,
|
||||
file: a.file,
|
||||
severity: 'warning'
|
||||
})),
|
||||
|
||||
// Data relationships without descriptions
|
||||
incompleteRelationships: (deepAnalysis.data_structures?.relationships || [])
|
||||
.filter(r => !r.description)
|
||||
.map(r => ({from: r.from, to: r.to, severity: 'info'})),
|
||||
|
||||
// Diagram validation issues
|
||||
diagramIssues: (deepAnalysis.diagrams?.validation || [])
|
||||
.filter(d => !d.valid)
|
||||
.map(d => ({file: d.file, issues: d.issues, severity: 'critical'}))
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Build Dynamic Questions
|
||||
|
||||
```javascript
|
||||
function buildComplianceQuestions(issues) {
|
||||
const questions = [];
|
||||
|
||||
if (issues.missingSections.length > 0) {
|
||||
questions.push({
|
||||
question: `发现 ${issues.missingSections.length} 个章节内容不完整,需要补充哪些?`,
|
||||
header: "章节补充",
|
||||
multiSelect: true,
|
||||
options: issues.missingSections.slice(0, 4).map(s => ({
|
||||
label: s.section,
|
||||
description: s.suggestion
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
if (issues.weakDescriptions.length > 0) {
|
||||
questions.push({
|
||||
question: `以下 ${issues.weakDescriptions.length} 个功能描述过于简短,请选择需要详细说明的:`,
|
||||
header: "功能描述",
|
||||
multiSelect: true,
|
||||
options: issues.weakDescriptions.slice(0, 4).map(f => ({
|
||||
label: f.feature,
|
||||
description: `当前:${f.current.substring(0, 30)}...`
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
if (issues.complexAlgorithms.length > 0) {
|
||||
questions.push({
|
||||
question: `发现 ${issues.complexAlgorithms.length} 个复杂算法缺少详细流程图,是否生成?`,
|
||||
header: "算法详解",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "全部生成 (推荐)", description: "为所有复杂算法生成含分支/循环的流程图"},
|
||||
{label: "仅最复杂的", description: `仅为 ${issues.complexAlgorithms[0]?.algorithm} 生成`},
|
||||
{label: "跳过", description: "保持当前简单流程图"}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
questions.push({
|
||||
question: "如何处理当前文档?",
|
||||
header: "操作",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "应用修改并继续", description: "应用上述选择,继续检查"},
|
||||
{label: "完成文档", description: "当前文档满足要求,生成最终版本"},
|
||||
{label: "重新分析", description: "使用不同配置重新分析代码"}
|
||||
]
|
||||
});
|
||||
|
||||
return questions.slice(0, 4);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Apply Updates
|
||||
|
||||
```javascript
|
||||
async function applyComplianceUpdates(responses, issues, analyses, outputDir) {
|
||||
const updates = [];
|
||||
|
||||
if (responses['章节补充']) {
|
||||
for (const section of responses['章节补充']) {
|
||||
const sectionAnalysis = await Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
prompt: `深入分析 ${section.section} 所需内容...`
|
||||
});
|
||||
updates.push({type: 'section_supplement', section: section.section, data: sectionAnalysis});
|
||||
}
|
||||
}
|
||||
|
||||
if (responses['算法详解'] === '全部生成 (推荐)') {
|
||||
for (const algo of issues.complexAlgorithms) {
|
||||
const detailedSteps = await analyzeAlgorithmInDepth(algo, analyses);
|
||||
const flowchart = generateAlgorithmFlowchart({
|
||||
name: algo.algorithm,
|
||||
inputs: detailedSteps.inputs,
|
||||
outputs: detailedSteps.outputs,
|
||||
steps: detailedSteps.steps
|
||||
});
|
||||
Write(`${outputDir}/diagrams/algorithm-${sanitizeId(algo.algorithm)}-detailed.mmd`, flowchart);
|
||||
updates.push({type: 'algorithm_flowchart', algorithm: algo.algorithm});
|
||||
}
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Iteration Loop
|
||||
|
||||
```javascript
|
||||
async function runComplianceLoop(documentPath, analyses, metadata, outputDir) {
|
||||
let iteration = 0;
|
||||
const maxIterations = 5;
|
||||
|
||||
while (iteration < maxIterations) {
|
||||
iteration++;
|
||||
|
||||
// Validate current document
|
||||
const document = Read(documentPath);
|
||||
const validation = validateCPCCCompliance(document, analyses);
|
||||
|
||||
// Extract issues
|
||||
const issues = extractComplianceIssues(validation, analyses);
|
||||
const totalIssues = Object.values(issues).flat().length;
|
||||
|
||||
if (totalIssues === 0) {
|
||||
console.log("✅ 所有检查通过,文档符合 CPCC 要求");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ask user
|
||||
const questions = buildComplianceQuestions(issues);
|
||||
const responses = await AskUserQuestion({questions});
|
||||
|
||||
if (responses['操作'] === '完成文档') break;
|
||||
if (responses['操作'] === '重新分析') return {action: 'restart'};
|
||||
|
||||
// Apply updates
|
||||
const updates = await applyComplianceUpdates(responses, issues, analyses, outputDir);
|
||||
|
||||
// Regenerate document
|
||||
const updatedDocument = regenerateDocument(document, updates, analyses);
|
||||
Write(documentPath, updatedDocument);
|
||||
|
||||
// Archive iteration
|
||||
Write(`${outputDir}/iterations/v${iteration}.md`, document);
|
||||
}
|
||||
|
||||
return {action: 'finalized', iterations: iteration};
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Final compliant document + iteration history in `iterations/`.
|
||||
121
.claude/skills/copyright-docs/specs/cpcc-requirements.md
Normal file
121
.claude/skills/copyright-docs/specs/cpcc-requirements.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# CPCC Compliance Requirements
|
||||
|
||||
China Copyright Protection Center (CPCC) requirements for software design specification.
|
||||
|
||||
## When to Use
|
||||
|
||||
| Phase | Usage | Section |
|
||||
|-------|-------|---------|
|
||||
| Phase 4 | Check document structure before assembly | Document Requirements, Mandatory Sections |
|
||||
| Phase 4 | Apply correct figure numbering | Figure Numbering Convention |
|
||||
| Phase 5 | Validate before each iteration | Validation Function |
|
||||
| Phase 5 | Handle failures during refinement | Error Handling |
|
||||
|
||||
---
|
||||
|
||||
## Document Requirements
|
||||
|
||||
### Format
|
||||
- [ ] 页眉包含软件名称和版本号
|
||||
- [ ] 页码位于右上角说明
|
||||
- [ ] 每页不少于30行文字(图表页除外)
|
||||
- [ ] A4纵向排版,文字从左至右
|
||||
|
||||
### Mandatory Sections (7 章节)
|
||||
- [ ] 1. 软件概述
|
||||
- [ ] 2. 系统架构图
|
||||
- [ ] 3. 功能模块设计
|
||||
- [ ] 4. 核心算法与流程
|
||||
- [ ] 5. 数据结构设计
|
||||
- [ ] 6. 接口设计
|
||||
- [ ] 7. 异常处理设计
|
||||
|
||||
### Content Requirements
|
||||
- [ ] 所有内容基于代码分析
|
||||
- [ ] 无臆测或未来计划
|
||||
- [ ] 无原始指令性文字
|
||||
- [ ] Mermaid 语法正确
|
||||
- [ ] 图表编号和说明完整
|
||||
|
||||
## Validation Function
|
||||
|
||||
```javascript
|
||||
function validateCPCCCompliance(document, analyses) {
|
||||
const checks = [
|
||||
{name: "软件概述完整性", pass: document.includes("## 1. 软件概述")},
|
||||
{name: "系统架构图存在", pass: document.includes("图2-1 系统架构图")},
|
||||
{name: "功能模块设计完整", pass: document.includes("## 3. 功能模块设计")},
|
||||
{name: "核心算法描述", pass: document.includes("## 4. 核心算法与流程")},
|
||||
{name: "数据结构设计", pass: document.includes("## 5. 数据结构设计")},
|
||||
{name: "接口设计说明", pass: document.includes("## 6. 接口设计")},
|
||||
{name: "异常处理设计", pass: document.includes("## 7. 异常处理设计")},
|
||||
{name: "Mermaid图表语法", pass: !document.includes("mermaid error")},
|
||||
{name: "页眉信息", pass: document.includes("页眉")},
|
||||
{name: "页码说明", pass: document.includes("页码")}
|
||||
];
|
||||
|
||||
return {
|
||||
passed: checks.filter(c => c.pass).length,
|
||||
total: checks.length,
|
||||
details: checks
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Software Categories
|
||||
|
||||
| Category | Document Focus |
|
||||
|----------|----------------|
|
||||
| 命令行工具 (CLI) | 命令、参数、使用流程 |
|
||||
| 后端服务/API | 端点、协议、数据流 |
|
||||
| SDK/库 | 接口、集成、使用示例 |
|
||||
| 数据处理系统 | 数据流、转换、ETL |
|
||||
| 自动化脚本 | 工作流、触发器、调度 |
|
||||
|
||||
## Figure Numbering Convention
|
||||
|
||||
| Section | Figure | Title |
|
||||
|---------|--------|-------|
|
||||
| 2 | 图2-1 | 系统架构图 |
|
||||
| 3 | 图3-1 | 功能模块结构图 |
|
||||
| 4 | 图4-N | {算法名称}流程图 |
|
||||
| 5 | 图5-1 | 数据结构类图 |
|
||||
| 6 | 图6-N | {接口名称}时序图 |
|
||||
| 7 | 图7-1 | 异常处理流程图 |
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Analysis timeout | Reduce scope, retry |
|
||||
| Missing section data | Re-run targeted agent |
|
||||
| Diagram validation fails | Regenerate with fixes |
|
||||
| User abandons iteration | Save progress, allow resume |
|
||||
|
||||
---
|
||||
|
||||
## Integration with Phases
|
||||
|
||||
**Phase 4 - Document Assembly**:
|
||||
```javascript
|
||||
// Before assembling document
|
||||
const docChecks = [
|
||||
{check: "页眉格式", value: `<!-- 页眉:${metadata.software_name} - 版本号:${metadata.version} -->`},
|
||||
{check: "页码说明", value: `<!-- 注:最终文档页码位于每页右上角 -->`}
|
||||
];
|
||||
|
||||
// Apply figure numbering from convention table
|
||||
const figureNumbers = getFigureNumbers(sectionIndex);
|
||||
```
|
||||
|
||||
**Phase 5 - Compliance Refinement**:
|
||||
```javascript
|
||||
// In 05-compliance-refinement.md
|
||||
const validation = validateCPCCCompliance(document, analyses);
|
||||
|
||||
if (validation.passed < validation.total) {
|
||||
// Failed checks become discovery questions
|
||||
const failedChecks = validation.details.filter(d => !d.pass);
|
||||
discoveries.complianceIssues = failedChecks;
|
||||
}
|
||||
```
|
||||
200
.claude/skills/copyright-docs/templates/agent-base.md
Normal file
200
.claude/skills/copyright-docs/templates/agent-base.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Agent Base Template
|
||||
|
||||
所有分析 Agent 的基础模板,确保一致性和高效执行。
|
||||
|
||||
## 通用提示词结构
|
||||
|
||||
```
|
||||
[ROLE] 你是{角色},专注于{职责}。
|
||||
|
||||
[TASK]
|
||||
分析代码库,生成 CPCC 合规的章节文档。
|
||||
- 输出: {output_dir}/sections/{filename}
|
||||
- 格式: Markdown + Mermaid
|
||||
- 范围: {scope_path}
|
||||
|
||||
[CONSTRAINTS]
|
||||
- 只描述已实现的代码,不臆测
|
||||
- 中文输出,技术术语可用英文
|
||||
- Mermaid 图表必须可渲染
|
||||
- 文件/类/函数需包含路径引用
|
||||
|
||||
[OUTPUT_FORMAT]
|
||||
1. 直接写入 MD 文件
|
||||
2. 返回 JSON 简要信息
|
||||
|
||||
[QUALITY_CHECKLIST]
|
||||
- [ ] 包含至少1个 Mermaid 图表
|
||||
- [ ] 每个子章节有实质内容 (>100字)
|
||||
- [ ] 代码引用格式: `src/path/file.ts:line`
|
||||
- [ ] 图表编号正确 (图N-M)
|
||||
```
|
||||
|
||||
## 变量说明
|
||||
|
||||
| 变量 | 来源 | 示例 |
|
||||
|------|------|------|
|
||||
| {output_dir} | Phase 1 创建 | .workflow/.scratchpad/copyright-xxx |
|
||||
| {software_name} | metadata.software_name | 智能数据分析系统 |
|
||||
| {scope_path} | metadata.scope_path | src/ |
|
||||
| {tech_stack} | metadata.tech_stack | TypeScript/Node.js |
|
||||
|
||||
## Agent 提示词模板
|
||||
|
||||
### 精简版 (推荐)
|
||||
|
||||
```javascript
|
||||
const agentPrompt = (agent, meta, outDir) => `
|
||||
[ROLE] ${AGENT_ROLES[agent]}
|
||||
|
||||
[TASK]
|
||||
分析 ${meta.scope_path},生成 ${AGENT_SECTIONS[agent]}。
|
||||
输出: ${outDir}/sections/${AGENT_FILES[agent]}
|
||||
|
||||
[TEMPLATE]
|
||||
${AGENT_TEMPLATES[agent]}
|
||||
|
||||
[FOCUS]
|
||||
${AGENT_FOCUS[agent].join('\n')}
|
||||
|
||||
[RETURN]
|
||||
{"status":"completed","output_file":"${AGENT_FILES[agent]}","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`;
|
||||
```
|
||||
|
||||
### 配置映射
|
||||
|
||||
```javascript
|
||||
const AGENT_ROLES = {
|
||||
architecture: "系统架构师,专注于分层设计和模块依赖",
|
||||
functions: "功能分析师,专注于功能点识别和交互",
|
||||
algorithms: "算法工程师,专注于核心逻辑和复杂度",
|
||||
data_structures: "数据建模师,专注于实体关系和类型",
|
||||
interfaces: "API设计师,专注于接口契约和协议",
|
||||
exceptions: "可靠性工程师,专注于异常处理和恢复"
|
||||
};
|
||||
|
||||
const AGENT_SECTIONS = {
|
||||
architecture: "Section 2: 系统架构图",
|
||||
functions: "Section 3: 功能模块设计",
|
||||
algorithms: "Section 4: 核心算法与流程",
|
||||
data_structures: "Section 5: 数据结构设计",
|
||||
interfaces: "Section 6: 接口设计",
|
||||
exceptions: "Section 7: 异常处理设计"
|
||||
};
|
||||
|
||||
const AGENT_FILES = {
|
||||
architecture: "section-2-architecture.md",
|
||||
functions: "section-3-functions.md",
|
||||
algorithms: "section-4-algorithms.md",
|
||||
data_structures: "section-5-data-structures.md",
|
||||
interfaces: "section-6-interfaces.md",
|
||||
exceptions: "section-7-exceptions.md"
|
||||
};
|
||||
|
||||
const AGENT_FOCUS = {
|
||||
architecture: [
|
||||
"1. 分层: 识别代码层次 (Controller/Service/Repository)",
|
||||
"2. 模块: 核心模块及职责边界",
|
||||
"3. 依赖: 模块间依赖方向",
|
||||
"4. 数据流: 请求/数据的流动路径"
|
||||
],
|
||||
functions: [
|
||||
"1. 功能点: 枚举所有用户可见功能",
|
||||
"2. 模块分组: 按业务域分组",
|
||||
"3. 入口: 每个功能的代码入口",
|
||||
"4. 交互: 功能间的调用关系"
|
||||
],
|
||||
algorithms: [
|
||||
"1. 核心算法: 业务逻辑的关键算法",
|
||||
"2. 流程步骤: 分支/循环/条件",
|
||||
"3. 复杂度: 时间/空间复杂度",
|
||||
"4. 输入输出: 参数和返回值"
|
||||
],
|
||||
data_structures: [
|
||||
"1. 实体: class/interface/type 定义",
|
||||
"2. 属性: 字段类型和可见性",
|
||||
"3. 关系: 继承/组合/关联",
|
||||
"4. 枚举: 枚举类型及其值"
|
||||
],
|
||||
interfaces: [
|
||||
"1. API端点: 路径/方法/说明",
|
||||
"2. 参数: 请求参数类型和校验",
|
||||
"3. 响应: 响应格式和状态码",
|
||||
"4. 时序: 典型调用流程"
|
||||
],
|
||||
exceptions: [
|
||||
"1. 异常类型: 自定义异常类",
|
||||
"2. 错误码: 错误码定义和含义",
|
||||
"3. 处理模式: try-catch/中间件",
|
||||
"4. 恢复策略: 重试/降级/告警"
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
## 效率优化
|
||||
|
||||
### 1. 减少冗余
|
||||
|
||||
**Before (冗余)**:
|
||||
```
|
||||
你是一个专业的系统架构师,具有丰富的软件设计经验。
|
||||
你需要分析代码库,识别系统的分层结构...
|
||||
```
|
||||
|
||||
**After (精简)**:
|
||||
```
|
||||
[ROLE] 系统架构师,专注于分层设计和模块依赖。
|
||||
[TASK] 分析 src/,生成系统架构图章节。
|
||||
```
|
||||
|
||||
### 2. 模板驱动
|
||||
|
||||
**Before (描述性)**:
|
||||
```
|
||||
请按照以下格式输出:
|
||||
首先写一个二级标题...
|
||||
然后添加一个Mermaid图...
|
||||
```
|
||||
|
||||
**After (模板)**:
|
||||
```
|
||||
[TEMPLATE]
|
||||
## 2. 系统架构图
|
||||
{intro}
|
||||
\`\`\`mermaid
|
||||
{diagram}
|
||||
\`\`\`
|
||||
**图2-1 系统架构图**
|
||||
### 2.1 {subsection}
|
||||
{content}
|
||||
```
|
||||
|
||||
### 3. 焦点明确
|
||||
|
||||
**Before (模糊)**:
|
||||
```
|
||||
分析项目的各个方面,包括架构、模块、依赖等
|
||||
```
|
||||
|
||||
**After (具体)**:
|
||||
```
|
||||
[FOCUS]
|
||||
1. 分层: Controller/Service/Repository
|
||||
2. 模块: 职责边界
|
||||
3. 依赖: 方向性
|
||||
4. 数据流: 路径
|
||||
```
|
||||
|
||||
### 4. 返回简洁
|
||||
|
||||
**Before (冗长)**:
|
||||
```
|
||||
请返回详细的分析结果,包括所有发现的问题...
|
||||
```
|
||||
|
||||
**After (结构化)**:
|
||||
```
|
||||
[RETURN]
|
||||
{"status":"completed","output_file":"xxx.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
```
|
||||
162
.claude/skills/project-analyze/SKILL.md
Normal file
162
.claude/skills/project-analyze/SKILL.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: project-analyze
|
||||
description: Multi-phase iterative project analysis with Mermaid diagrams. Generates architecture reports, design reports, method analysis reports. Use when analyzing codebases, understanding project structure, reviewing architecture, exploring design patterns, or documenting system components. Triggers on "analyze project", "architecture report", "design analysis", "code structure", "system overview".
|
||||
allowed-tools: Task, AskUserQuestion, Read, Bash, Glob, Grep, Write
|
||||
---
|
||||
|
||||
# Project Analysis Skill
|
||||
|
||||
Generate comprehensive project analysis reports through multi-phase iterative workflow.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Context-Optimized Architecture │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Phase 1: Requirements → analysis-config.json │
|
||||
│ ↓ │
|
||||
│ Phase 2: Exploration → 初步探索,确定范围 │
|
||||
│ ↓ │
|
||||
│ Phase 3: Parallel Agents → sections/section-*.md (直接写MD) │
|
||||
│ ↓ 返回简要JSON │
|
||||
│ Phase 3.5: Consolidation → consolidation-summary.md │
|
||||
│ Agent ↓ 返回质量评分+问题列表 │
|
||||
│ ↓ │
|
||||
│ Phase 4: Assembly → 合并MD + 质量附录 │
|
||||
│ ↓ │
|
||||
│ Phase 5: Refinement → 最终报告 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **Agent 直接输出 MD**: 避免 JSON → MD 转换的上下文开销
|
||||
2. **简要返回**: Agent 只返回路径+摘要,不返回完整内容
|
||||
3. **汇总 Agent**: 独立 Agent 负责跨章节问题检测和质量评分
|
||||
4. **引用合并**: Phase 4 读取文件合并,不在上下文中传递
|
||||
5. **段落式描述**: 禁止清单罗列,层层递进,客观学术表达
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Phase 1: Requirements Discovery │
|
||||
│ → Read: phases/01-requirements-discovery.md │
|
||||
│ → Collect: report type, depth level, scope, focus areas │
|
||||
│ → Output: analysis-config.json │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 2: Project Exploration │
|
||||
│ → Read: phases/02-project-exploration.md │
|
||||
│ → Launch: parallel exploration agents │
|
||||
│ → Output: exploration context for Phase 3 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 3: Deep Analysis (Parallel Agents) │
|
||||
│ → Read: phases/03-deep-analysis.md │
|
||||
│ → Reference: specs/quality-standards.md │
|
||||
│ → Each Agent: 分析代码 → 直接写 sections/section-*.md │
|
||||
│ → Return: {"status", "output_file", "summary", "cross_notes"} │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 3.5: Consolidation (New!) │
|
||||
│ → Read: phases/03.5-consolidation.md │
|
||||
│ → Input: Agent 返回的简要信息 + cross_module_notes │
|
||||
│ → Analyze: 一致性/完整性/关联性/质量检查 │
|
||||
│ → Output: consolidation-summary.md │
|
||||
│ → Return: {"quality_score", "issues", "stats"} │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 4: Report Generation │
|
||||
│ → Read: phases/04-report-generation.md │
|
||||
│ → Check: 如有 errors,提示用户处理 │
|
||||
│ → Merge: Executive Summary + sections/*.md + 质量附录 │
|
||||
│ → Output: {TYPE}-REPORT.md │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 5: Iterative Refinement │
|
||||
│ → Read: phases/05-iterative-refinement.md │
|
||||
│ → Reference: specs/quality-standards.md │
|
||||
│ → Loop: 发现问题 → 提问 → 修复 → 重新检查 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Report Types
|
||||
|
||||
| Type | Output | Agents | Focus |
|
||||
|------|--------|--------|-------|
|
||||
| `architecture` | ARCHITECTURE-REPORT.md | 5 | System structure, modules, dependencies |
|
||||
| `design` | DESIGN-REPORT.md | 4 | Patterns, classes, interfaces |
|
||||
| `methods` | METHODS-REPORT.md | 4 | Algorithms, critical paths, APIs |
|
||||
| `comprehensive` | COMPREHENSIVE-REPORT.md | All | All above combined |
|
||||
|
||||
## Agent Configuration by Report Type
|
||||
|
||||
### Architecture Report
|
||||
| Agent | Output File | Section |
|
||||
|-------|-------------|---------|
|
||||
| overview | section-overview.md | System Overview |
|
||||
| layers | section-layers.md | Layer Analysis |
|
||||
| dependencies | section-dependencies.md | Module Dependencies |
|
||||
| dataflow | section-dataflow.md | Data Flow |
|
||||
| entrypoints | section-entrypoints.md | Entry Points |
|
||||
|
||||
### Design Report
|
||||
| Agent | Output File | Section |
|
||||
|-------|-------------|---------|
|
||||
| patterns | section-patterns.md | Design Patterns |
|
||||
| classes | section-classes.md | Class Relationships |
|
||||
| interfaces | section-interfaces.md | Interface Contracts |
|
||||
| state | section-state.md | State Management |
|
||||
|
||||
### Methods Report
|
||||
| Agent | Output File | Section |
|
||||
|-------|-------------|---------|
|
||||
| algorithms | section-algorithms.md | Core Algorithms |
|
||||
| paths | section-paths.md | Critical Code Paths |
|
||||
| apis | section-apis.md | Public API Reference |
|
||||
| logic | section-logic.md | Complex Logic |
|
||||
|
||||
## Directory Setup
|
||||
|
||||
```javascript
|
||||
// 生成时间戳目录名
|
||||
const timestamp = new Date().toISOString().slice(0,19).replace(/[-:T]/g, '');
|
||||
const dir = `.workflow/.scratchpad/analyze-${timestamp}`;
|
||||
|
||||
// Windows (cmd)
|
||||
Bash(`mkdir "${dir}\\sections"`);
|
||||
Bash(`mkdir "${dir}\\iterations"`);
|
||||
|
||||
// Unix/macOS
|
||||
// Bash(`mkdir -p "${dir}/sections" "${dir}/iterations"`);
|
||||
```
|
||||
|
||||
## Output Structure
|
||||
|
||||
```
|
||||
.workflow/.scratchpad/analyze-{timestamp}/
|
||||
├── analysis-config.json # Phase 1
|
||||
├── sections/ # Phase 3 (Agent 直接写入)
|
||||
│ ├── section-overview.md
|
||||
│ ├── section-layers.md
|
||||
│ ├── section-dependencies.md
|
||||
│ └── ...
|
||||
├── consolidation-summary.md # Phase 3.5
|
||||
├── {TYPE}-REPORT.md # Final Output
|
||||
└── iterations/ # Phase 5
|
||||
├── v1.md
|
||||
└── v2.md
|
||||
```
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [phases/01-requirements-discovery.md](phases/01-requirements-discovery.md) | User interaction, config collection |
|
||||
| [phases/02-project-exploration.md](phases/02-project-exploration.md) | Initial exploration |
|
||||
| [phases/03-deep-analysis.md](phases/03-deep-analysis.md) | Parallel agent analysis |
|
||||
| [phases/03.5-consolidation.md](phases/03.5-consolidation.md) | Cross-section consolidation |
|
||||
| [phases/04-report-generation.md](phases/04-report-generation.md) | Report assembly |
|
||||
| [phases/05-iterative-refinement.md](phases/05-iterative-refinement.md) | Quality refinement |
|
||||
| [specs/quality-standards.md](specs/quality-standards.md) | Quality gates, standards |
|
||||
| [specs/writing-style.md](specs/writing-style.md) | 段落式学术写作规范 |
|
||||
| [../_shared/mermaid-utils.md](../_shared/mermaid-utils.md) | Shared Mermaid utilities |
|
||||
@@ -0,0 +1,79 @@
|
||||
# Phase 1: Requirements Discovery
|
||||
|
||||
Collect user requirements before analysis begins.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Report Type Selection
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "What type of project analysis report would you like?",
|
||||
header: "Report Type",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Architecture (Recommended)", description: "System structure, module relationships, layer analysis, dependency graph"},
|
||||
{label: "Design", description: "Design patterns, class relationships, component interactions, abstraction analysis"},
|
||||
{label: "Methods", description: "Key algorithms, critical code paths, core function explanations with examples"},
|
||||
{label: "Comprehensive", description: "All above combined into a complete project analysis"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Step 2: Depth Level Selection
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "What depth level do you need?",
|
||||
header: "Depth",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Overview", description: "High-level understanding, suitable for onboarding"},
|
||||
{label: "Detailed", description: "In-depth analysis with code examples"},
|
||||
{label: "Deep-Dive", description: "Exhaustive analysis with implementation details"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Step 3: Scope Definition
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "What scope should the analysis cover?",
|
||||
header: "Scope",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Full Project", description: "Analyze entire codebase"},
|
||||
{label: "Specific Module", description: "Focus on a specific module or directory"},
|
||||
{label: "Custom Path", description: "Specify custom path pattern"}
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
## Focus Areas Mapping
|
||||
|
||||
| Report Type | Focus Areas |
|
||||
|-------------|-------------|
|
||||
| Architecture | Layer Structure, Module Dependencies, Entry Points, Data Flow |
|
||||
| Design | Design Patterns, Class Relationships, Interface Contracts, State Management |
|
||||
| Methods | Core Algorithms, Critical Paths, Public APIs, Complex Logic |
|
||||
| Comprehensive | All above combined |
|
||||
|
||||
## Output
|
||||
|
||||
Save configuration to `analysis-config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "architecture|design|methods|comprehensive",
|
||||
"depth": "overview|detailed|deep-dive",
|
||||
"scope": "**/*|src/**/*|custom",
|
||||
"focus_areas": ["..."]
|
||||
}
|
||||
```
|
||||
176
.claude/skills/project-analyze/phases/02-project-exploration.md
Normal file
176
.claude/skills/project-analyze/phases/02-project-exploration.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Phase 2: Project Exploration
|
||||
|
||||
Launch parallel exploration agents based on report type and task context.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Intelligent Angle Selection
|
||||
|
||||
```javascript
|
||||
// Angle presets based on report type (adapted from lite-plan.md)
|
||||
const ANGLE_PRESETS = {
|
||||
architecture: ['layer-structure', 'module-dependencies', 'entry-points', 'data-flow'],
|
||||
design: ['design-patterns', 'class-relationships', 'interface-contracts', 'state-management'],
|
||||
methods: ['core-algorithms', 'critical-paths', 'public-apis', 'complex-logic'],
|
||||
comprehensive: ['architecture', 'patterns', 'dependencies', 'integration-points']
|
||||
};
|
||||
|
||||
// Depth-based angle count
|
||||
const angleCount = {
|
||||
shallow: 2,
|
||||
standard: 3,
|
||||
deep: 4
|
||||
};
|
||||
|
||||
function selectAngles(reportType, depth) {
|
||||
const preset = ANGLE_PRESETS[reportType] || ANGLE_PRESETS.comprehensive;
|
||||
const count = angleCount[depth] || 3;
|
||||
return preset.slice(0, count);
|
||||
}
|
||||
|
||||
const selectedAngles = selectAngles(config.type, config.depth);
|
||||
|
||||
console.log(`
|
||||
## Exploration Plan
|
||||
|
||||
Report Type: ${config.type}
|
||||
Depth: ${config.depth}
|
||||
Selected Angles: ${selectedAngles.join(', ')}
|
||||
|
||||
Launching ${selectedAngles.length} parallel explorations...
|
||||
`);
|
||||
```
|
||||
|
||||
### Step 2: Launch Parallel Agents (Direct Output)
|
||||
|
||||
**⚠️ CRITICAL**: Agents write output files directly. No aggregation needed.
|
||||
|
||||
```javascript
|
||||
// Launch agents with pre-assigned angles
|
||||
const explorationTasks = selectedAngles.map((angle, index) =>
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false, // ⚠️ MANDATORY: Must wait for results
|
||||
description: `Explore: ${angle}`,
|
||||
prompt: `
|
||||
## Exploration Objective
|
||||
Execute **${angle}** exploration for ${config.type} project analysis report.
|
||||
|
||||
## Assigned Context
|
||||
- **Exploration Angle**: ${angle}
|
||||
- **Report Type**: ${config.type}
|
||||
- **Depth**: ${config.depth}
|
||||
- **Scope**: ${config.scope}
|
||||
- **Exploration Index**: ${index + 1} of ${selectedAngles.length}
|
||||
- **Output File**: ${sessionFolder}/exploration-${angle}.json
|
||||
|
||||
## MANDATORY FIRST STEPS (Execute by Agent)
|
||||
**You (cli-explore-agent) MUST execute these steps in order:**
|
||||
1. Run: ccw tool exec get_modules_by_depth '{}' (project structure)
|
||||
2. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files)
|
||||
3. Analyze project from ${angle} perspective
|
||||
|
||||
## Exploration Strategy (${angle} focus)
|
||||
|
||||
**Step 1: Structural Scan** (Bash)
|
||||
- get_modules_by_depth.sh → identify modules related to ${angle}
|
||||
- find/rg → locate files relevant to ${angle} aspect
|
||||
- Analyze imports/dependencies from ${angle} perspective
|
||||
|
||||
**Step 2: Semantic Analysis** (Gemini/Qwen CLI)
|
||||
- How does existing code handle ${angle} concerns?
|
||||
- What patterns are used for ${angle}?
|
||||
- Identify key architectural decisions related to ${angle}
|
||||
|
||||
**Step 3: Write Output Directly**
|
||||
- Consolidate ${angle} findings into JSON
|
||||
- Write to output file path specified above
|
||||
|
||||
## Expected Output Schema
|
||||
|
||||
**File**: ${sessionFolder}/exploration-${angle}.json
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"angle": "${angle}",
|
||||
"findings": {
|
||||
"structure": [
|
||||
{ "component": "...", "type": "module|layer|service", "description": "..." }
|
||||
],
|
||||
"patterns": [
|
||||
{ "name": "...", "usage": "...", "files": ["path1", "path2"] }
|
||||
],
|
||||
"relationships": [
|
||||
{ "from": "...", "to": "...", "type": "depends|imports|calls", "strength": "high|medium|low" }
|
||||
],
|
||||
"key_files": [
|
||||
{ "path": "src/file.ts", "relevance": 0.85, "rationale": "Core ${angle} logic" }
|
||||
]
|
||||
},
|
||||
"insights": [
|
||||
{ "observation": "...", "impact": "high|medium|low", "recommendation": "..." }
|
||||
],
|
||||
"_metadata": {
|
||||
"exploration_angle": "${angle}",
|
||||
"exploration_index": ${index + 1},
|
||||
"report_type": "${config.type}",
|
||||
"timestamp": "ISO8601"
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Success Criteria
|
||||
- [ ] get_modules_by_depth.sh executed
|
||||
- [ ] At least 3 relevant files identified with ${angle} rationale
|
||||
- [ ] Patterns are actionable (code examples, not generic advice)
|
||||
- [ ] Relationships include concrete file references
|
||||
- [ ] JSON output written to ${sessionFolder}/exploration-${angle}.json
|
||||
- [ ] Return: 2-3 sentence summary of ${angle} findings
|
||||
`
|
||||
})
|
||||
);
|
||||
|
||||
// Execute all exploration tasks in parallel
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Session folder structure after exploration:
|
||||
|
||||
```
|
||||
${sessionFolder}/
|
||||
├── exploration-{angle1}.json # Agent 1 direct output
|
||||
├── exploration-{angle2}.json # Agent 2 direct output
|
||||
├── exploration-{angle3}.json # Agent 3 direct output (if applicable)
|
||||
└── exploration-{angle4}.json # Agent 4 direct output (if applicable)
|
||||
```
|
||||
|
||||
## Downstream Usage (Phase 3 Analysis Input)
|
||||
|
||||
Subsequent analysis phases MUST read exploration outputs as input:
|
||||
|
||||
```javascript
|
||||
// Discover exploration files by known angle pattern
|
||||
const explorationData = {};
|
||||
selectedAngles.forEach(angle => {
|
||||
const filePath = `${sessionFolder}/exploration-${angle}.json`;
|
||||
explorationData[angle] = JSON.parse(Read(filePath));
|
||||
});
|
||||
|
||||
// Pass to analysis agent
|
||||
Task({
|
||||
subagent_type: "analysis-agent",
|
||||
prompt: `
|
||||
## Analysis Input
|
||||
|
||||
### Exploration Data by Angle
|
||||
${Object.entries(explorationData).map(([angle, data]) => `
|
||||
#### ${angle}
|
||||
${JSON.stringify(data, null, 2)}
|
||||
`).join('\n')}
|
||||
|
||||
## Analysis Task
|
||||
Synthesize findings from all exploration angles...
|
||||
`
|
||||
});
|
||||
```
|
||||
854
.claude/skills/project-analyze/phases/03-deep-analysis.md
Normal file
854
.claude/skills/project-analyze/phases/03-deep-analysis.md
Normal file
@@ -0,0 +1,854 @@
|
||||
# Phase 3: Deep Analysis
|
||||
|
||||
并行 Agent 撰写设计报告章节,返回简要信息。
|
||||
|
||||
> **规范参考**: [../specs/quality-standards.md](../specs/quality-standards.md)
|
||||
> **写作风格**: [../specs/writing-style.md](../specs/writing-style.md)
|
||||
|
||||
## Exploration → Agent 自动分配
|
||||
|
||||
根据 Phase 2 生成的 exploration 文件名自动分配对应的 analysis agent。
|
||||
|
||||
### 映射规则
|
||||
|
||||
```javascript
|
||||
// Exploration 角度 → Agent 映射(基于文件名识别,不读取内容)
|
||||
const EXPLORATION_TO_AGENT = {
|
||||
// Architecture Report 角度
|
||||
'layer-structure': 'layers',
|
||||
'module-dependencies': 'dependencies',
|
||||
'entry-points': 'entrypoints',
|
||||
'data-flow': 'dataflow',
|
||||
|
||||
// Design Report 角度
|
||||
'design-patterns': 'patterns',
|
||||
'class-relationships': 'classes',
|
||||
'interface-contracts': 'interfaces',
|
||||
'state-management': 'state',
|
||||
|
||||
// Methods Report 角度
|
||||
'core-algorithms': 'algorithms',
|
||||
'critical-paths': 'paths',
|
||||
'public-apis': 'apis',
|
||||
'complex-logic': 'logic',
|
||||
|
||||
// Comprehensive 角度
|
||||
'architecture': 'overview',
|
||||
'patterns': 'patterns',
|
||||
'dependencies': 'dependencies',
|
||||
'integration-points': 'entrypoints'
|
||||
};
|
||||
|
||||
// 从文件名提取角度
|
||||
function extractAngle(filename) {
|
||||
// exploration-layer-structure.json → layer-structure
|
||||
const match = filename.match(/exploration-(.+)\.json$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
// 分配 agent
|
||||
function assignAgent(explorationFile) {
|
||||
const angle = extractAngle(path.basename(explorationFile));
|
||||
return EXPLORATION_TO_AGENT[angle] || null;
|
||||
}
|
||||
|
||||
// Agent 配置(用于 buildAgentPrompt)
|
||||
const AGENT_CONFIGS = {
|
||||
overview: {
|
||||
role: '首席系统架构师',
|
||||
task: '基于代码库全貌,撰写"总体架构"章节,洞察核心价值主张和顶层技术决策',
|
||||
focus: '领域边界与定位、架构范式、核心技术决策、顶层模块划分',
|
||||
constraint: '避免罗列目录结构,重点阐述设计意图,包含至少1个 Mermaid 架构图'
|
||||
},
|
||||
layers: {
|
||||
role: '资深软件设计师',
|
||||
task: '分析系统逻辑分层结构,撰写"逻辑视点与分层架构"章节',
|
||||
focus: '职责分配体系、数据流向与约束、边界隔离策略、异常处理流',
|
||||
constraint: '不要列举具体文件名,关注层级间契约和隔离艺术'
|
||||
},
|
||||
dependencies: {
|
||||
role: '集成架构专家',
|
||||
task: '审视系统外部连接与内部耦合,撰写"依赖管理与生态集成"章节',
|
||||
focus: '外部集成拓扑、核心依赖分析、依赖注入与控制反转、供应链安全',
|
||||
constraint: '禁止简单列出依赖配置,必须分析集成策略和风险控制模型'
|
||||
},
|
||||
dataflow: {
|
||||
role: '数据架构师',
|
||||
task: '追踪系统数据流转机制,撰写"数据流与状态管理"章节',
|
||||
focus: '数据入口与出口、数据转换管道、持久化策略、一致性保障',
|
||||
constraint: '关注数据生命周期和形态演变,不要罗列数据库表结构'
|
||||
},
|
||||
entrypoints: {
|
||||
role: '系统边界分析师',
|
||||
task: '识别系统入口设计和关键路径,撰写"系统入口与调用链"章节',
|
||||
focus: '入口类型与职责、请求处理管道、关键业务路径、异常与边界处理',
|
||||
constraint: '关注入口设计哲学,不要逐个列举所有端点'
|
||||
},
|
||||
patterns: {
|
||||
role: '核心开发规范制定者',
|
||||
task: '挖掘代码中的复用机制和标准化实践,撰写"设计模式与工程规范"章节',
|
||||
focus: '架构级模式、通信与并发模式、横切关注点实现、抽象与复用策略',
|
||||
constraint: '避免教科书式解释,必须结合项目上下文说明应用场景'
|
||||
},
|
||||
classes: {
|
||||
role: '领域模型设计师',
|
||||
task: '分析系统类型体系和领域模型,撰写"类型体系与领域建模"章节',
|
||||
focus: '领域模型设计、继承与组合策略、职责分配原则、类型安全与约束',
|
||||
constraint: '关注建模思想,用 UML 类图辅助说明核心关系'
|
||||
},
|
||||
interfaces: {
|
||||
role: '契约设计专家',
|
||||
task: '分析系统接口设计和抽象层次,撰写"接口契约与抽象设计"章节',
|
||||
focus: '抽象层次设计、契约与实现分离、扩展点设计、版本演进策略',
|
||||
constraint: '关注接口设计哲学,不要逐个列举接口方法签名'
|
||||
},
|
||||
state: {
|
||||
role: '状态管理架构师',
|
||||
task: '分析系统状态管理机制,撰写"状态管理与生命周期"章节',
|
||||
focus: '状态模型设计、状态生命周期、并发与一致性、状态恢复与容错',
|
||||
constraint: '关注状态管理设计决策,不要列举具体变量名'
|
||||
},
|
||||
algorithms: {
|
||||
role: '算法架构师',
|
||||
task: '分析系统核心算法设计,撰写"核心算法与计算模型"章节',
|
||||
focus: '算法选型与权衡、计算模型设计、性能与可扩展性、正确性保障',
|
||||
constraint: '关注算法思想,用流程图辅助说明复杂逻辑'
|
||||
},
|
||||
paths: {
|
||||
role: '性能架构师',
|
||||
task: '分析系统关键执行路径,撰写"关键路径与性能设计"章节',
|
||||
focus: '关键业务路径、性能敏感区域、瓶颈识别与缓解、降级与熔断',
|
||||
constraint: '关注路径设计战略考量,不要罗列所有代码执行步骤'
|
||||
},
|
||||
apis: {
|
||||
role: 'API 设计规范专家',
|
||||
task: '分析系统对外接口设计规范,撰写"API 设计与规范"章节',
|
||||
focus: 'API 设计风格、命名与结构规范、版本管理策略、错误处理规范',
|
||||
constraint: '关注设计规范和一致性,不要逐个列举所有 API 端点'
|
||||
},
|
||||
logic: {
|
||||
role: '业务逻辑架构师',
|
||||
task: '分析系统业务逻辑建模,撰写"业务逻辑与规则引擎"章节',
|
||||
focus: '业务规则建模、决策点设计、边界条件处理、业务流程编排',
|
||||
constraint: '关注业务逻辑组织方式,不要逐行解释代码逻辑'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 自动发现与分配流程
|
||||
|
||||
```javascript
|
||||
// 1. 发现所有 exploration 文件(仅看文件名)
|
||||
const explorationFiles = bash(`find ${sessionFolder} -name "exploration-*.json" -type f`)
|
||||
.split('\n')
|
||||
.filter(f => f.trim());
|
||||
|
||||
// 2. 按文件名自动分配 agent
|
||||
const agentAssignments = explorationFiles.map(file => {
|
||||
const angle = extractAngle(path.basename(file));
|
||||
const agentName = EXPLORATION_TO_AGENT[angle];
|
||||
return {
|
||||
exploration_file: file,
|
||||
angle: angle,
|
||||
agent: agentName,
|
||||
output_file: `section-${agentName}.md`
|
||||
};
|
||||
}).filter(a => a.agent); // 过滤未映射的角度
|
||||
|
||||
console.log(`
|
||||
## Agent Auto-Assignment
|
||||
|
||||
Found ${explorationFiles.length} exploration files:
|
||||
${agentAssignments.map(a => `- ${a.angle} → ${a.agent} agent`).join('\n')}
|
||||
`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent 执行前置条件
|
||||
|
||||
**每个 Agent 接收 exploration 文件路径,自行读取内容**:
|
||||
|
||||
```javascript
|
||||
// Agent prompt 中包含文件路径
|
||||
// Agent 启动后的操作顺序:
|
||||
// 1. Read exploration 文件(上下文输入)
|
||||
// 2. Read 规范文件
|
||||
// 3. 执行分析任务
|
||||
```
|
||||
|
||||
规范文件路径(相对于 skill 根目录):
|
||||
- `specs/quality-standards.md` - 质量标准和检查清单
|
||||
- `specs/writing-style.md` - 段落式写作规范
|
||||
|
||||
---
|
||||
|
||||
## 通用写作规范(所有 Agent 共用)
|
||||
|
||||
```
|
||||
[STYLE]
|
||||
- **语言规范**:使用严谨、专业的中文进行技术写作。仅专业术语(如 Singleton, Middleware, ORM)保留英文原文。
|
||||
- **叙述视角**:采用完全客观的第三人称视角("上帝视角")。严禁使用"我们"、"开发者"、"用户"、"你"或"我"。主语应为"系统"、"模块"、"设计"、"架构"或"该层"。
|
||||
- **段落结构**:
|
||||
- 禁止使用无序列表作为主要叙述方式,必须将观点融合在连贯的段落中。
|
||||
- 采用"论点-论据-结论"的逻辑结构。
|
||||
- 善用逻辑连接词("因此"、"然而"、"鉴于"、"进而")来体现设计思路的推演过程。
|
||||
- **内容深度**:
|
||||
- 抽象化:描述"做什么"和"为什么这么做",而不是"怎么写的"。
|
||||
- 方法论:强调设计模式、架构原则(如 SOLID、高内聚低耦合)的应用。
|
||||
- 非代码化:除非定义关键接口,否则不直接引用代码。文件引用仅作为括号内的来源标注 (参考: path/to/file)。
|
||||
```
|
||||
|
||||
## Agent 配置
|
||||
|
||||
### Architecture Report Agents
|
||||
|
||||
| Agent | 输出文件 | 关注点 |
|
||||
|-------|----------|--------|
|
||||
| overview | section-overview.md | 顶层架构、技术决策、设计哲学 |
|
||||
| layers | section-layers.md | 逻辑分层、职责边界、隔离策略 |
|
||||
| dependencies | section-dependencies.md | 依赖治理、集成拓扑、风险控制 |
|
||||
| dataflow | section-dataflow.md | 数据流向、转换机制、一致性保障 |
|
||||
| entrypoints | section-entrypoints.md | 入口设计、调用链、异常传播 |
|
||||
|
||||
### Design Report Agents
|
||||
|
||||
| Agent | 输出文件 | 关注点 |
|
||||
|-------|----------|--------|
|
||||
| patterns | section-patterns.md | 架构模式、通信机制、横切关注点 |
|
||||
| classes | section-classes.md | 类型体系、继承策略、职责划分 |
|
||||
| interfaces | section-interfaces.md | 契约设计、抽象层次、扩展机制 |
|
||||
| state | section-state.md | 状态模型、生命周期、并发控制 |
|
||||
|
||||
### Methods Report Agents
|
||||
|
||||
| Agent | 输出文件 | 关注点 |
|
||||
|-------|----------|--------|
|
||||
| algorithms | section-algorithms.md | 核心算法思想、复杂度权衡、优化策略 |
|
||||
| paths | section-paths.md | 关键路径设计、性能敏感点、瓶颈分析 |
|
||||
| apis | section-apis.md | API 设计规范、版本策略、兼容性 |
|
||||
| logic | section-logic.md | 业务逻辑建模、决策机制、边界处理 |
|
||||
|
||||
---
|
||||
|
||||
## Agent 返回格式
|
||||
|
||||
```typescript
|
||||
interface AgentReturn {
|
||||
status: "completed" | "partial" | "failed";
|
||||
output_file: string;
|
||||
summary: string; // 50字以内
|
||||
cross_module_notes: string[]; // 跨模块发现
|
||||
stats: { diagrams: number; };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent 提示词
|
||||
|
||||
### Overview Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 首席系统架构师
|
||||
|
||||
[TASK]
|
||||
基于代码库的全貌,撰写《系统架构设计报告》的"总体架构"章节。透过代码表象,洞察系统的核心价值主张和顶层技术决策。
|
||||
输出: ${outDir}/sections/section-overview.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作,专业术语保留英文
|
||||
- 完全客观的第三人称视角,严禁"我们"、"开发者"
|
||||
- 段落式叙述,采用"论点-论据-结论"结构
|
||||
- 善用逻辑连接词体现设计推演过程
|
||||
- 描述"做什么"和"为什么",非"怎么写的"
|
||||
- 不直接引用代码,文件仅作来源标注
|
||||
|
||||
[FOCUS]
|
||||
- 领域边界与定位:系统旨在解决什么核心业务问题?其在更大的技术生态中处于什么位置?
|
||||
- 架构范式:采用何种架构风格(分层、六边形、微服务、事件驱动等)?选择该范式的根本原因是什么?
|
||||
- 核心技术决策:关键技术栈的选型依据,这些选型如何支撑系统的非功能性需求(性能、扩展性、维护性)
|
||||
- 顶层模块划分:系统在最高层级被划分为哪些逻辑单元?它们之间的高层协作机制是怎样的?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 避免罗列目录结构
|
||||
- 重点阐述"设计意图"而非"现有功能"
|
||||
- 包含至少1个 Mermaid 架构图辅助说明
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-overview.md","summary":"<50字>","cross_module_notes":[],"stats":{"diagrams":1}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Layers Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 资深软件设计师
|
||||
|
||||
[TASK]
|
||||
分析系统的逻辑分层结构,撰写《系统架构设计报告》的"逻辑视点与分层架构"章节。重点揭示系统如何通过分层来隔离关注点。
|
||||
输出: ${outDir}/sections/section-layers.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角,主语为"系统"、"该层"、"设计"
|
||||
- 段落式叙述,禁止无序列表作为主体
|
||||
- 强调方法论和架构原则的应用
|
||||
|
||||
[FOCUS]
|
||||
- 职责分配体系:系统被划分为哪几个逻辑层级?每一层的核心职责和输入输出是什么?
|
||||
- 数据流向与约束:数据在各层之间是如何流动的?是否存在严格的单向依赖规则?
|
||||
- 边界隔离策略:各层之间通过何种方式解耦(接口抽象、DTO转换、依赖注入)?如何防止下层实现细节泄露到上层?
|
||||
- 异常处理流:异常信息如何在分层结构中传递和转化?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 不要列举具体的文件名列表
|
||||
- 关注"层级间的契约"和"隔离的艺术"
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-layers.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Dependencies Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 集成架构专家
|
||||
|
||||
[TASK]
|
||||
审视系统的外部连接与内部耦合情况,撰写《系统架构设计报告》的"依赖管理与生态集成"章节。
|
||||
输出: ${outDir}/sections/section-dependencies.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述,逻辑连贯
|
||||
|
||||
[FOCUS]
|
||||
- 外部集成拓扑:系统如何与外部世界(第三方API、数据库、中间件)交互?采用了何种适配器或防腐层设计来隔离外部变化?
|
||||
- 核心依赖分析:区分"核心业务依赖"与"基础设施依赖"。系统对关键框架的依赖程度如何?是否存在被锁定的风险?
|
||||
- 依赖注入与控制反转:系统内部模块间的组装方式是什么?是否实现了依赖倒置原则以支持可测试性?
|
||||
- 供应链安全与治理:对于复杂的依赖树,系统采用了何种策略来管理版本和兼容性?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 禁止简单列出依赖配置文件的内容
|
||||
- 必须分析依赖背后的"集成策略"和"风险控制模型"
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-dependencies.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Patterns Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 核心开发规范制定者
|
||||
|
||||
[TASK]
|
||||
挖掘代码中的复用机制和标准化实践,撰写《系统架构设计报告》的"设计模式与工程规范"章节。
|
||||
输出: ${outDir}/sections/section-patterns.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述,结合项目上下文
|
||||
|
||||
[FOCUS]
|
||||
- 架构级模式:识别系统中广泛使用的架构模式(CQRS、Event Sourcing、Repository Pattern、Unit of Work)。阐述引入这些模式解决了什么特定难题
|
||||
- 通信与并发模式:分析组件间的通信机制(同步/异步、观察者模式、发布订阅)以及并发控制策略
|
||||
- 横切关注点实现:系统如何统一处理日志、鉴权、缓存、事务管理等横切逻辑(AOP、中间件管道、装饰器)?
|
||||
- 抽象与复用策略:分析基类、泛型、工具类的设计思想,系统如何通过抽象来减少重复代码并提高一致性?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 避免教科书式地解释设计模式定义,必须结合当前项目上下文说明其应用场景
|
||||
- 关注"解决类问题的通用机制"
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-patterns.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### DataFlow Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 数据架构师
|
||||
|
||||
[TASK]
|
||||
追踪系统的数据流转机制,撰写《系统架构设计报告》的"数据流与状态管理"章节。
|
||||
输出: ${outDir}/sections/section-dataflow.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 数据入口与出口:数据从何处进入系统,最终流向何处?边界处的数据校验和转换策略是什么?
|
||||
- 数据转换管道:数据在各层/模块间经历了怎样的形态变化?DTO、Entity、VO 等数据对象的职责边界如何划分?
|
||||
- 持久化策略:系统如何设计数据存储方案?采用了何种 ORM 策略或数据访问模式?
|
||||
- 一致性保障:系统如何处理事务边界?分布式场景下如何保证数据一致性?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注数据的"生命周期"和"形态演变"
|
||||
- 不要罗列数据库表结构
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-dataflow.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### EntryPoints Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 系统边界分析师
|
||||
|
||||
[TASK]
|
||||
识别系统的入口设计和关键路径,撰写《系统架构设计报告》的"系统入口与调用链"章节。
|
||||
输出: ${outDir}/sections/section-entrypoints.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 入口类型与职责:系统提供了哪些类型的入口(REST API、CLI、消息队列消费者、定时任务)?各入口的设计目的和适用场景是什么?
|
||||
- 请求处理管道:从入口到核心逻辑,请求经过了怎样的处理管道?中间件/拦截器的编排逻辑是什么?
|
||||
- 关键业务路径:最重要的几条业务流程的调用链是怎样的?关键节点的设计考量是什么?
|
||||
- 异常与边界处理:系统如何统一处理异常?异常信息如何传播和转化?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"入口的设计哲学"而非 API 清单
|
||||
- 不要逐个列举所有端点
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-entrypoints.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Classes Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 领域模型设计师
|
||||
|
||||
[TASK]
|
||||
分析系统的类型体系和领域模型,撰写《系统架构设计报告》的"类型体系与领域建模"章节。
|
||||
输出: ${outDir}/sections/section-classes.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 领域模型设计:系统的核心领域概念有哪些?它们之间的关系如何建模(聚合、实体、值对象)?
|
||||
- 继承与组合策略:系统倾向于使用继承还是组合?基类/接口的设计意图是什么?
|
||||
- 职责分配原则:类的职责划分遵循了什么原则?是否体现了单一职责原则?
|
||||
- 类型安全与约束:系统如何利用类型系统来表达业务约束和不变量?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"建模思想"而非类的属性列表
|
||||
- 用 UML 类图辅助说明核心关系
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-classes.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Interfaces Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 契约设计专家
|
||||
|
||||
[TASK]
|
||||
分析系统的接口设计和抽象层次,撰写《系统架构设计报告》的"接口契约与抽象设计"章节。
|
||||
输出: ${outDir}/sections/section-interfaces.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 抽象层次设计:系统定义了哪些核心接口/抽象类?这些抽象的设计意图和职责边界是什么?
|
||||
- 契约与实现分离:接口如何隔离契约与实现?多态机制如何被运用?
|
||||
- 扩展点设计:系统预留了哪些扩展点?如何在不修改核心代码的情况下扩展功能?
|
||||
- 版本演进策略:接口如何支持版本演进?向后兼容性如何保障?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"接口的设计哲学"
|
||||
- 不要逐个列举接口方法签名
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-interfaces.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### State Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 状态管理架构师
|
||||
|
||||
[TASK]
|
||||
分析系统的状态管理机制,撰写《系统架构设计报告》的"状态管理与生命周期"章节。
|
||||
输出: ${outDir}/sections/section-state.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 状态模型设计:系统需要管理哪些类型的状态(会话状态、应用状态、领域状态)?状态的存储位置和作用域是什么?
|
||||
- 状态生命周期:状态如何创建、更新、销毁?生命周期管理的机制是什么?
|
||||
- 并发与一致性:多线程/多实例场景下,状态如何保持一致?采用了何种并发控制策略?
|
||||
- 状态恢复与容错:系统如何处理状态丢失或损坏?是否有状态恢复机制?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"状态管理的设计决策"
|
||||
- 不要列举具体的变量名
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-state.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Algorithms Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 算法架构师
|
||||
|
||||
[TASK]
|
||||
分析系统的核心算法设计,撰写《系统架构设计报告》的"核心算法与计算模型"章节。
|
||||
输出: ${outDir}/sections/section-algorithms.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 算法选型与权衡:系统的核心业务逻辑采用了哪些关键算法?选择这些算法的考量因素是什么(时间复杂度、空间复杂度、可维护性)?
|
||||
- 计算模型设计:复杂计算如何被分解和组织?是否采用了流水线、Map-Reduce 等计算模式?
|
||||
- 性能与可扩展性:算法设计如何考虑性能和可扩展性?是否有针对大数据量的优化策略?
|
||||
- 正确性保障:关键算法的正确性如何保障?是否有边界条件的特殊处理?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"算法思想"而非具体实现代码
|
||||
- 用流程图辅助说明复杂逻辑
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-algorithms.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Paths Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 性能架构师
|
||||
|
||||
[TASK]
|
||||
分析系统的关键执行路径,撰写《系统架构设计报告》的"关键路径与性能设计"章节。
|
||||
输出: ${outDir}/sections/section-paths.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 关键业务路径:系统中最重要的几条业务执行路径是什么?这些路径的设计目标和约束是什么?
|
||||
- 性能敏感区域:哪些环节是性能敏感的?系统采用了何种优化策略(缓存、异步、批处理)?
|
||||
- 瓶颈识别与缓解:潜在的性能瓶颈在哪里?设计中是否预留了扩展空间?
|
||||
- 降级与熔断:在高负载或故障场景下,系统如何保护关键路径?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"路径设计的战略考量"
|
||||
- 不要罗列所有代码执行步骤
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-paths.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### APIs Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] API 设计规范专家
|
||||
|
||||
[TASK]
|
||||
分析系统的对外接口设计规范,撰写《系统架构设计报告》的"API 设计与规范"章节。
|
||||
输出: ${outDir}/sections/section-apis.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- API 设计风格:系统采用了何种 API 设计风格(RESTful、GraphQL、RPC)?选择该风格的原因是什么?
|
||||
- 命名与结构规范:API 的命名、路径结构、参数设计遵循了什么规范?是否有一致性保障机制?
|
||||
- 版本管理策略:API 如何支持版本演进?向后兼容性策略是什么?
|
||||
- 错误处理规范:API 错误响应的设计规范是什么?错误码体系如何组织?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"设计规范和一致性"
|
||||
- 不要逐个列举所有 API 端点
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-apis.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Logic Agent
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[SPEC]
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
[ROLE] 业务逻辑架构师
|
||||
|
||||
[TASK]
|
||||
分析系统的业务逻辑建模,撰写《系统架构设计报告》的"业务逻辑与规则引擎"章节。
|
||||
输出: ${outDir}/sections/section-logic.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作
|
||||
- 客观第三人称视角
|
||||
- 段落式叙述
|
||||
|
||||
[FOCUS]
|
||||
- 业务规则建模:核心业务规则如何被表达和组织?是否采用了规则引擎或策略模式?
|
||||
- 决策点设计:系统中的关键决策点有哪些?决策逻辑如何被封装和测试?
|
||||
- 边界条件处理:系统如何处理边界条件和异常情况?是否有防御性编程措施?
|
||||
- 业务流程编排:复杂业务流程如何被编排?是否采用了工作流引擎或状态机?
|
||||
|
||||
[CONSTRAINT]
|
||||
- 关注"业务逻辑的组织方式"
|
||||
- 不要逐行解释代码逻辑
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-logic.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 执行流程
|
||||
|
||||
```javascript
|
||||
// 1. 发现 exploration 文件并自动分配 agent
|
||||
const explorationFiles = bash(`find ${sessionFolder} -name "exploration-*.json" -type f`)
|
||||
.split('\n')
|
||||
.filter(f => f.trim());
|
||||
|
||||
const agentAssignments = explorationFiles.map(file => {
|
||||
const angle = extractAngle(path.basename(file));
|
||||
const agentName = EXPLORATION_TO_AGENT[angle];
|
||||
return { exploration_file: file, angle, agent: agentName };
|
||||
}).filter(a => a.agent);
|
||||
|
||||
// 2. 准备目录
|
||||
Bash(`mkdir "${outputDir}\\sections"`);
|
||||
|
||||
// 3. 并行启动所有 Agent(传递 exploration 文件路径)
|
||||
const results = await Promise.all(
|
||||
agentAssignments.map(assignment =>
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
description: `Analyze: ${assignment.agent}`,
|
||||
prompt: buildAgentPrompt(assignment, config, outputDir)
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// 4. 收集简要返回信息
|
||||
const summaries = results.map(r => JSON.parse(r));
|
||||
|
||||
// 5. 传递给 Phase 3.5 汇总 Agent
|
||||
return { summaries, cross_notes: summaries.flatMap(s => s.cross_module_notes) };
|
||||
```
|
||||
|
||||
### Agent Prompt 构建
|
||||
|
||||
```javascript
|
||||
function buildAgentPrompt(assignment, config, outputDir) {
|
||||
const agentConfig = AGENT_CONFIGS[assignment.agent];
|
||||
return `
|
||||
[CONTEXT]
|
||||
**Exploration 文件**: ${assignment.exploration_file}
|
||||
首先读取此文件获取 ${assignment.angle} 探索结果作为分析上下文。
|
||||
|
||||
[SPEC]
|
||||
读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
|
||||
[ROLE] ${agentConfig.role}
|
||||
|
||||
[TASK]
|
||||
${agentConfig.task}
|
||||
输出: ${outputDir}/sections/section-${assignment.agent}.md
|
||||
|
||||
[STYLE]
|
||||
- 严谨专业的中文技术写作,专业术语保留英文
|
||||
- 完全客观的第三人称视角,严禁"我们"、"开发者"
|
||||
- 段落式叙述,采用"论点-论据-结论"结构
|
||||
- 善用逻辑连接词体现设计推演过程
|
||||
|
||||
[FOCUS]
|
||||
${agentConfig.focus}
|
||||
|
||||
[CONSTRAINT]
|
||||
${agentConfig.constraint}
|
||||
|
||||
[RETURN JSON]
|
||||
{"status":"completed","output_file":"section-${assignment.agent}.md","summary":"<50字>","cross_module_notes":[],"stats":{}}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
各 Agent 写入 `sections/section-xxx.md`,返回简要 JSON 供 Phase 3.5 汇总。
|
||||
233
.claude/skills/project-analyze/phases/03.5-consolidation.md
Normal file
233
.claude/skills/project-analyze/phases/03.5-consolidation.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# Phase 3.5: Consolidation Agent
|
||||
|
||||
汇总所有分析 Agent 的产出,生成跨章节综合分析,为 Phase 4 索引报告提供内容。
|
||||
|
||||
> **写作规范**: [../specs/writing-style.md](../specs/writing-style.md)
|
||||
|
||||
## 执行要求
|
||||
|
||||
**必须执行**:Phase 3 所有 Analysis Agents 完成后,主编排器**必须**调用此 Consolidation Agent。
|
||||
|
||||
**触发条件**:
|
||||
- Phase 3 所有 agent 已返回结果(status: completed/partial/failed)
|
||||
- `sections/section-*.md` 文件已生成
|
||||
|
||||
**输入来源**:
|
||||
- `agent_summaries`: Phase 3 各 agent 返回的 JSON(包含 status, output_file, summary, cross_module_notes)
|
||||
- `cross_module_notes`: 从各 agent 返回中提取的跨模块备注数组
|
||||
|
||||
**调用时机**:
|
||||
```javascript
|
||||
// Phase 3 完成后,主编排器执行:
|
||||
const phase3Results = await runPhase3Agents(); // 并行执行所有 analysis agents
|
||||
const agentSummaries = phase3Results.map(r => JSON.parse(r));
|
||||
const crossNotes = agentSummaries.flatMap(s => s.cross_module_notes || []);
|
||||
|
||||
// 必须调用 Phase 3.5 Consolidation Agent
|
||||
await runPhase35Consolidation(agentSummaries, crossNotes);
|
||||
```
|
||||
|
||||
## 核心职责
|
||||
|
||||
1. **跨章节综合分析**:生成 synthesis(报告综述)
|
||||
2. **章节摘要提取**:生成 section_summaries(索引表格内容)
|
||||
3. **质量检查**:识别问题并评分
|
||||
4. **建议汇总**:生成 recommendations(优先级排序)
|
||||
|
||||
## 输入
|
||||
|
||||
```typescript
|
||||
interface ConsolidationInput {
|
||||
output_dir: string;
|
||||
config: AnalysisConfig;
|
||||
agent_summaries: AgentReturn[];
|
||||
cross_module_notes: string[];
|
||||
}
|
||||
```
|
||||
|
||||
## Agent 调用代码
|
||||
|
||||
主编排器使用以下代码调用 Consolidation Agent:
|
||||
|
||||
```javascript
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
## 规范前置
|
||||
首先读取规范文件:
|
||||
- Read: ${skillRoot}/specs/quality-standards.md
|
||||
- Read: ${skillRoot}/specs/writing-style.md
|
||||
严格遵循规范中的质量标准和段落式写作要求。
|
||||
|
||||
## 任务
|
||||
作为汇总 Agent,读取所有章节文件,执行跨章节分析,生成汇总报告和索引内容。
|
||||
|
||||
## 输入
|
||||
- 章节文件: ${outputDir}/sections/section-*.md
|
||||
- Agent 摘要: ${JSON.stringify(agent_summaries)}
|
||||
- 跨模块备注: ${JSON.stringify(cross_module_notes)}
|
||||
- 报告类型: ${config.type}
|
||||
|
||||
## 核心产出
|
||||
|
||||
### 1. 综合分析 (synthesis)
|
||||
阅读所有章节,用 2-3 段落描述项目全貌:
|
||||
- 第一段:项目定位与核心架构特征
|
||||
- 第二段:关键设计决策与技术选型
|
||||
- 第三段:整体质量评价与显著特点
|
||||
|
||||
### 2. 章节摘要 (section_summaries)
|
||||
为每个章节提取一句话核心发现,用于索引表格。
|
||||
|
||||
### 3. 架构洞察 (cross_analysis)
|
||||
描述章节间的关联性,如:
|
||||
- 模块间的依赖关系如何体现在各章节
|
||||
- 设计决策如何贯穿多个层面
|
||||
- 潜在的一致性或冲突
|
||||
|
||||
### 4. 建议汇总 (recommendations)
|
||||
按优先级整理各章节的建议,段落式描述。
|
||||
|
||||
## 质量检查维度
|
||||
|
||||
### 一致性检查
|
||||
- 术语一致性:同一概念是否使用相同名称
|
||||
- 代码引用:file:line 格式是否正确
|
||||
|
||||
### 完整性检查
|
||||
- 章节覆盖:是否涵盖所有必需章节
|
||||
- 内容深度:每章节是否达到 ${config.depth} 级别
|
||||
|
||||
### 质量检查
|
||||
- Mermaid 语法:图表是否可渲染
|
||||
- 段落式写作:是否符合写作规范(禁止清单罗列)
|
||||
|
||||
## 输出文件
|
||||
|
||||
写入: ${outputDir}/consolidation-summary.md
|
||||
|
||||
### 文件格式
|
||||
|
||||
\`\`\`markdown
|
||||
# 分析汇总报告
|
||||
|
||||
## 综合分析
|
||||
|
||||
[2-3 段落的项目全貌描述,段落式写作]
|
||||
|
||||
## 章节摘要
|
||||
|
||||
| 章节 | 文件 | 核心发现 |
|
||||
|------|------|----------|
|
||||
| 系统概述 | section-overview.md | 一句话描述 |
|
||||
| 层次分析 | section-layers.md | 一句话描述 |
|
||||
| ... | ... | ... |
|
||||
|
||||
## 架构洞察
|
||||
|
||||
[跨章节关联分析,段落式描述]
|
||||
|
||||
## 建议汇总
|
||||
|
||||
[优先级排序的建议,段落式描述]
|
||||
|
||||
---
|
||||
|
||||
## 质量评估
|
||||
|
||||
### 评分
|
||||
|
||||
| 维度 | 得分 | 说明 |
|
||||
|------|------|------|
|
||||
| 完整性 | 85% | ... |
|
||||
| 一致性 | 90% | ... |
|
||||
| 深度 | 95% | ... |
|
||||
| 可读性 | 88% | ... |
|
||||
| 综合 | 89% | ... |
|
||||
|
||||
### 发现的问题
|
||||
|
||||
#### 严重问题
|
||||
| ID | 类型 | 位置 | 描述 |
|
||||
|----|------|------|------|
|
||||
| E001 | ... | ... | ... |
|
||||
|
||||
#### 警告
|
||||
| ID | 类型 | 位置 | 描述 |
|
||||
|----|------|------|------|
|
||||
| W001 | ... | ... | ... |
|
||||
|
||||
#### 提示
|
||||
| ID | 类型 | 位置 | 描述 |
|
||||
|----|------|------|------|
|
||||
| I001 | ... | ... | ... |
|
||||
|
||||
### 统计
|
||||
|
||||
- 章节数: X
|
||||
- 图表数: X
|
||||
- 总字数: X
|
||||
\`\`\`
|
||||
|
||||
## 返回格式 (JSON)
|
||||
|
||||
{
|
||||
"status": "completed",
|
||||
"output_file": "consolidation-summary.md",
|
||||
|
||||
// Phase 4 索引报告所需
|
||||
"synthesis": "2-3 段落的综合分析文本",
|
||||
"cross_analysis": "跨章节关联分析文本",
|
||||
"recommendations": "优先级排序的建议文本",
|
||||
"section_summaries": [
|
||||
{"file": "section-overview.md", "title": "系统概述", "summary": "一句话核心发现"},
|
||||
{"file": "section-layers.md", "title": "层次分析", "summary": "一句话核心发现"}
|
||||
],
|
||||
|
||||
// 质量信息
|
||||
"quality_score": {
|
||||
"completeness": 85,
|
||||
"consistency": 90,
|
||||
"depth": 95,
|
||||
"readability": 88,
|
||||
"overall": 89
|
||||
},
|
||||
"issues": {
|
||||
"errors": [...],
|
||||
"warnings": [...],
|
||||
"info": [...]
|
||||
},
|
||||
"stats": {
|
||||
"total_sections": 5,
|
||||
"total_diagrams": 8,
|
||||
"total_words": 3500
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
## 问题分类
|
||||
|
||||
| 严重级别 | 前缀 | 含义 | 处理方式 |
|
||||
|----------|------|------|----------|
|
||||
| Error | E | 阻塞报告生成 | 必须修复 |
|
||||
| Warning | W | 影响报告质量 | 建议修复 |
|
||||
| Info | I | 可改进项 | 可选修复 |
|
||||
|
||||
## 问题类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| missing | 缺失章节 |
|
||||
| inconsistency | 术语/描述不一致 |
|
||||
| invalid_ref | 无效代码引用 |
|
||||
| syntax | Mermaid 语法错误 |
|
||||
| shallow | 内容过浅 |
|
||||
| list_style | 违反段落式写作规范 |
|
||||
|
||||
## Output
|
||||
|
||||
- **文件**: `consolidation-summary.md`(完整汇总报告)
|
||||
- **返回**: JSON 包含 Phase 4 所需的所有字段
|
||||
217
.claude/skills/project-analyze/phases/04-report-generation.md
Normal file
217
.claude/skills/project-analyze/phases/04-report-generation.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Phase 4: Report Generation
|
||||
|
||||
生成索引式报告,通过 markdown 链接引用章节文件。
|
||||
|
||||
> **规范参考**: [../specs/quality-standards.md](../specs/quality-standards.md)
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **引用而非嵌入**:主报告通过链接引用章节,不复制内容
|
||||
2. **索引 + 综述**:主报告提供导航和高阶分析
|
||||
3. **避免重复**:综述来自 consolidation,不重新生成
|
||||
4. **独立可读**:各章节文件可单独阅读
|
||||
|
||||
## 输入
|
||||
|
||||
```typescript
|
||||
interface ReportInput {
|
||||
output_dir: string;
|
||||
config: AnalysisConfig;
|
||||
consolidation: {
|
||||
quality_score: QualityScore;
|
||||
issues: { errors: Issue[], warnings: Issue[], info: Issue[] };
|
||||
stats: Stats;
|
||||
synthesis: string; // consolidation agent 的综合分析
|
||||
section_summaries: Array<{file: string, summary: string}>;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 执行流程
|
||||
|
||||
```javascript
|
||||
// 1. 质量门禁检查
|
||||
if (consolidation.issues.errors.length > 0) {
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: `发现 ${consolidation.issues.errors.length} 个严重问题,如何处理?`,
|
||||
header: "质量检查",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "查看并修复", description: "显示问题列表,手动修复后重试"},
|
||||
{label: "忽略继续", description: "跳过问题检查,继续装配"},
|
||||
{label: "终止", description: "停止报告生成"}
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (response === "查看并修复") {
|
||||
return { action: "fix_required", errors: consolidation.issues.errors };
|
||||
}
|
||||
if (response === "终止") {
|
||||
return { action: "abort" };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 生成索引式报告(不读取章节内容)
|
||||
const report = generateIndexReport(config, consolidation);
|
||||
|
||||
// 3. 写入最终文件
|
||||
const fileName = `${config.type.toUpperCase()}-REPORT.md`;
|
||||
Write(`${outputDir}/${fileName}`, report);
|
||||
```
|
||||
|
||||
## 报告模板
|
||||
|
||||
### 通用结构
|
||||
|
||||
```markdown
|
||||
# {报告标题}
|
||||
|
||||
> 生成日期:{date}
|
||||
> 分析范围:{scope}
|
||||
> 分析深度:{depth}
|
||||
> 质量评分:{overall}%
|
||||
|
||||
---
|
||||
|
||||
## 报告综述
|
||||
|
||||
{consolidation.synthesis - 来自汇总 Agent 的跨章节综合分析}
|
||||
|
||||
---
|
||||
|
||||
## 章节索引
|
||||
|
||||
| 章节 | 核心发现 | 详情 |
|
||||
|------|----------|------|
|
||||
{section_summaries 生成的表格行}
|
||||
|
||||
---
|
||||
|
||||
## 架构洞察
|
||||
|
||||
{从 consolidation 提取的跨模块关联分析}
|
||||
|
||||
---
|
||||
|
||||
## 建议与展望
|
||||
|
||||
{consolidation.recommendations - 优先级排序的综合建议}
|
||||
|
||||
---
|
||||
|
||||
**附录**
|
||||
|
||||
- [质量报告](./consolidation-summary.md)
|
||||
- [章节文件目录](./sections/)
|
||||
```
|
||||
|
||||
### 报告标题映射
|
||||
|
||||
| 类型 | 标题 |
|
||||
|------|------|
|
||||
| architecture | 项目架构设计报告 |
|
||||
| design | 项目设计模式报告 |
|
||||
| methods | 项目核心方法报告 |
|
||||
| comprehensive | 项目综合分析报告 |
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generateIndexReport(config, consolidation) {
|
||||
const titles = {
|
||||
architecture: "项目架构设计报告",
|
||||
design: "项目设计模式报告",
|
||||
methods: "项目核心方法报告",
|
||||
comprehensive: "项目综合分析报告"
|
||||
};
|
||||
|
||||
const date = new Date().toLocaleDateString('zh-CN');
|
||||
|
||||
// 章节索引表格
|
||||
const sectionTable = consolidation.section_summaries
|
||||
.map(s => `| ${s.title} | ${s.summary} | [查看详情](./sections/${s.file}) |`)
|
||||
.join('\n');
|
||||
|
||||
return `# ${titles[config.type]}
|
||||
|
||||
> 生成日期:${date}
|
||||
> 分析范围:${config.scope}
|
||||
> 分析深度:${config.depth}
|
||||
> 质量评分:${consolidation.quality_score.overall}%
|
||||
|
||||
---
|
||||
|
||||
## 报告综述
|
||||
|
||||
${consolidation.synthesis}
|
||||
|
||||
---
|
||||
|
||||
## 章节索引
|
||||
|
||||
| 章节 | 核心发现 | 详情 |
|
||||
|------|----------|------|
|
||||
${sectionTable}
|
||||
|
||||
---
|
||||
|
||||
## 架构洞察
|
||||
|
||||
${consolidation.cross_analysis || '详见各章节分析。'}
|
||||
|
||||
---
|
||||
|
||||
## 建议与展望
|
||||
|
||||
${consolidation.recommendations || '详见质量报告中的改进建议。'}
|
||||
|
||||
---
|
||||
|
||||
**附录**
|
||||
|
||||
- [质量报告](./consolidation-summary.md)
|
||||
- [章节文件目录](./sections/)
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
## 输出结构
|
||||
|
||||
```
|
||||
.workflow/.scratchpad/analyze-{timestamp}/
|
||||
├── sections/ # 独立章节(Phase 3 产出)
|
||||
│ ├── section-overview.md
|
||||
│ ├── section-layers.md
|
||||
│ └── ...
|
||||
├── consolidation-summary.md # 质量报告(Phase 3.5 产出)
|
||||
└── {TYPE}-REPORT.md # 索引报告(本阶段产出)
|
||||
```
|
||||
|
||||
## 与 Phase 3.5 的协作
|
||||
|
||||
Phase 3.5 consolidation agent 需要提供:
|
||||
|
||||
```typescript
|
||||
interface ConsolidationOutput {
|
||||
// ... 原有字段
|
||||
synthesis: string; // 跨章节综合分析(2-3 段落)
|
||||
cross_analysis: string; // 架构级关联洞察
|
||||
recommendations: string; // 优先级排序的建议
|
||||
section_summaries: Array<{
|
||||
file: string; // 文件名
|
||||
title: string; // 章节标题
|
||||
summary: string; // 一句话核心发现
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
## 关键变更
|
||||
|
||||
| 原设计 | 新设计 |
|
||||
|--------|--------|
|
||||
| 读取章节内容并拼接 | 链接引用,不读取内容 |
|
||||
| 重新生成 Executive Summary | 直接使用 consolidation.synthesis |
|
||||
| 嵌入质量评分表格 | 链接引用 consolidation-summary.md |
|
||||
| 主报告包含全部内容 | 主报告仅为索引 + 综述 |
|
||||
124
.claude/skills/project-analyze/phases/05-iterative-refinement.md
Normal file
124
.claude/skills/project-analyze/phases/05-iterative-refinement.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Phase 5: Iterative Refinement
|
||||
|
||||
Discovery-driven refinement based on analysis findings.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Extract Discoveries
|
||||
|
||||
```javascript
|
||||
function extractDiscoveries(deepAnalysis) {
|
||||
return {
|
||||
ambiguities: deepAnalysis.findings.filter(f => f.confidence < 0.7),
|
||||
complexityHotspots: deepAnalysis.findings.filter(f => f.complexity === 'high'),
|
||||
patternDeviations: deepAnalysis.patterns.filter(p => p.consistency < 0.8),
|
||||
unclearDependencies: deepAnalysis.dependencies.filter(d => d.type === 'implicit'),
|
||||
potentialIssues: deepAnalysis.recommendations.filter(r => r.priority === 'investigate'),
|
||||
depthOpportunities: deepAnalysis.sections.filter(s => s.has_more_detail)
|
||||
};
|
||||
}
|
||||
|
||||
const discoveries = extractDiscoveries(deepAnalysis);
|
||||
```
|
||||
|
||||
### Step 2: Build Dynamic Questions
|
||||
|
||||
Questions emerge from discoveries, NOT predetermined:
|
||||
|
||||
```javascript
|
||||
function buildDynamicQuestions(discoveries, config) {
|
||||
const questions = [];
|
||||
|
||||
if (discoveries.ambiguities.length > 0) {
|
||||
questions.push({
|
||||
question: `Analysis found ambiguity in "${discoveries.ambiguities[0].area}". Which interpretation is correct?`,
|
||||
header: "Clarify",
|
||||
options: discoveries.ambiguities[0].interpretations
|
||||
});
|
||||
}
|
||||
|
||||
if (discoveries.complexityHotspots.length > 0) {
|
||||
questions.push({
|
||||
question: `These areas have high complexity. Which would you like explained?`,
|
||||
header: "Deep-Dive",
|
||||
multiSelect: true,
|
||||
options: discoveries.complexityHotspots.slice(0, 4).map(h => ({
|
||||
label: h.name,
|
||||
description: h.summary
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
if (discoveries.patternDeviations.length > 0) {
|
||||
questions.push({
|
||||
question: `Found pattern deviations. Should these be highlighted in the report?`,
|
||||
header: "Patterns",
|
||||
options: [
|
||||
{label: "Yes, include analysis", description: "Add section explaining deviations"},
|
||||
{label: "No, skip", description: "Omit from report"}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Always include action question
|
||||
questions.push({
|
||||
question: "How would you like to proceed?",
|
||||
header: "Action",
|
||||
options: [
|
||||
{label: "Continue refining", description: "Address more discoveries"},
|
||||
{label: "Finalize report", description: "Generate final output"},
|
||||
{label: "Change scope", description: "Modify analysis scope"}
|
||||
]
|
||||
});
|
||||
|
||||
return questions.slice(0, 4); // Max 4 questions
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Apply Refinements
|
||||
|
||||
```javascript
|
||||
if (userAction === "Continue refining") {
|
||||
// Apply selected refinements
|
||||
for (const selection of userSelections) {
|
||||
applyRefinement(selection, deepAnalysis, report);
|
||||
}
|
||||
|
||||
// Save iteration
|
||||
Write(`${outputDir}/iterations/iteration-${iterationCount}.json`, {
|
||||
timestamp: new Date().toISOString(),
|
||||
discoveries: discoveries,
|
||||
selections: userSelections,
|
||||
changes: appliedChanges
|
||||
});
|
||||
|
||||
// Loop back to Step 1
|
||||
iterationCount++;
|
||||
goto Step1;
|
||||
}
|
||||
|
||||
if (userAction === "Finalize report") {
|
||||
// Proceed to final output
|
||||
goto FinalizeReport;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Finalize Report
|
||||
|
||||
```javascript
|
||||
// Add iteration history to report metadata
|
||||
const finalReport = {
|
||||
...report,
|
||||
metadata: {
|
||||
iterations: iterationCount,
|
||||
refinements_applied: allRefinements,
|
||||
final_discoveries: discoveries
|
||||
}
|
||||
};
|
||||
|
||||
Write(`${outputDir}/${config.type.toUpperCase()}-REPORT.md`, finalReport);
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Updated report with refinements, saved iterations to `iterations/` folder.
|
||||
115
.claude/skills/project-analyze/specs/quality-standards.md
Normal file
115
.claude/skills/project-analyze/specs/quality-standards.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Quality Standards
|
||||
|
||||
Quality gates and requirements for project analysis reports.
|
||||
|
||||
## When to Use
|
||||
|
||||
| Phase | Usage | Section |
|
||||
|-------|-------|---------|
|
||||
| Phase 4 | Check report structure before assembly | Report Requirements |
|
||||
| Phase 5 | Validate before each iteration | Quality Gates |
|
||||
| Phase 5 | Handle failures during refinement | Error Handling |
|
||||
|
||||
---
|
||||
|
||||
## Report Requirements
|
||||
|
||||
**Use in Phase 4**: Ensure report includes all required elements.
|
||||
|
||||
| Requirement | Check | How to Fix |
|
||||
|-------------|-------|------------|
|
||||
| Executive Summary | 3-5 key takeaways | Extract from analysis findings |
|
||||
| Visual diagrams | Valid Mermaid syntax | Use `../_shared/mermaid-utils.md` |
|
||||
| Code references | `file:line` format | Link to actual source locations |
|
||||
| Recommendations | Actionable, specific | Derive from analysis insights |
|
||||
| Consistent depth | Match user's depth level | Adjust detail per config.depth |
|
||||
|
||||
---
|
||||
|
||||
## Quality Gates
|
||||
|
||||
**Use in Phase 5**: Run these checks before asking user questions.
|
||||
|
||||
```javascript
|
||||
function runQualityGates(report, config, diagrams) {
|
||||
const gates = [
|
||||
{
|
||||
name: "focus_areas_covered",
|
||||
check: () => config.focus_areas.every(area =>
|
||||
report.toLowerCase().includes(area.toLowerCase())
|
||||
),
|
||||
fix: "Re-analyze missing focus areas"
|
||||
},
|
||||
{
|
||||
name: "diagrams_valid",
|
||||
check: () => diagrams.every(d => d.valid),
|
||||
fix: "Regenerate failed diagrams with mermaid-utils"
|
||||
},
|
||||
{
|
||||
name: "code_refs_accurate",
|
||||
check: () => extractCodeRefs(report).every(ref => fileExists(ref)),
|
||||
fix: "Update invalid file references"
|
||||
},
|
||||
{
|
||||
name: "no_placeholders",
|
||||
check: () => !report.includes('[TODO]') && !report.includes('[PLACEHOLDER]'),
|
||||
fix: "Fill in all placeholder content"
|
||||
},
|
||||
{
|
||||
name: "recommendations_specific",
|
||||
check: () => !report.includes('consider') || report.includes('specifically'),
|
||||
fix: "Make recommendations project-specific"
|
||||
}
|
||||
];
|
||||
|
||||
const results = gates.map(g => ({...g, passed: g.check()}));
|
||||
const allPassed = results.every(r => r.passed);
|
||||
|
||||
return { allPassed, results };
|
||||
}
|
||||
```
|
||||
|
||||
**Integration with Phase 5**:
|
||||
```javascript
|
||||
// In 05-iterative-refinement.md
|
||||
const { allPassed, results } = runQualityGates(report, config, diagrams);
|
||||
|
||||
if (allPassed) {
|
||||
// All gates passed → ask user to confirm or finalize
|
||||
} else {
|
||||
// Gates failed → include failed gates in discovery questions
|
||||
const failedGates = results.filter(r => !r.passed);
|
||||
discoveries.qualityIssues = failedGates;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Use when**: Encountering errors during any phase.
|
||||
|
||||
| Error | Detection | Recovery |
|
||||
|-------|-----------|----------|
|
||||
| CLI timeout | Bash exits with timeout | Reduce scope via `config.scope`, retry |
|
||||
| Exploration failure | Agent returns error | Fall back to `Read` + `Grep` directly |
|
||||
| User abandons | User selects "cancel" | Save to `iterations/`, allow resume |
|
||||
| Invalid scope path | Path doesn't exist | `AskUserQuestion` to correct path |
|
||||
| Diagram validation fails | `validateMermaidSyntax` returns issues | Regenerate with stricter escaping |
|
||||
|
||||
**Recovery Flow**:
|
||||
```javascript
|
||||
try {
|
||||
await executePhase(phase);
|
||||
} catch (error) {
|
||||
const recovery = ERROR_HANDLERS[error.type];
|
||||
if (recovery) {
|
||||
await recovery.action(error, config);
|
||||
// Retry phase or continue
|
||||
} else {
|
||||
// Save progress and ask user
|
||||
Write(`${outputDir}/error-state.json`, { phase, error, config });
|
||||
AskUserQuestion({ question: "遇到错误,如何处理?", ... });
|
||||
}
|
||||
}
|
||||
```
|
||||
152
.claude/skills/project-analyze/specs/writing-style.md
Normal file
152
.claude/skills/project-analyze/specs/writing-style.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 写作风格规范
|
||||
|
||||
## 核心原则
|
||||
|
||||
**段落式描述,层层递进,禁止清单罗列。**
|
||||
|
||||
## 禁止的写作模式
|
||||
|
||||
```markdown
|
||||
<!-- 禁止:清单罗列 -->
|
||||
### 模块列表
|
||||
- 用户模块:处理用户相关功能
|
||||
- 订单模块:处理订单相关功能
|
||||
- 支付模块:处理支付相关功能
|
||||
|
||||
### 依赖关系
|
||||
| 模块 | 依赖 | 说明 |
|
||||
|------|------|------|
|
||||
| A | B | xxx |
|
||||
```
|
||||
|
||||
## 推荐的写作模式
|
||||
|
||||
```markdown
|
||||
<!-- 推荐:段落式描述 -->
|
||||
### 模块架构设计
|
||||
|
||||
系统采用分层模块化架构,核心业务逻辑围绕用户、订单、支付三大领域展开。
|
||||
用户模块作为系统的入口层,承担身份认证与权限管理职责,为下游模块提供
|
||||
统一的用户上下文。订单模块位于业务核心层,依赖用户模块获取会话信息,
|
||||
并协调支付模块完成交易闭环。
|
||||
|
||||
值得注意的是,支付模块采用策略模式实现多渠道支付,通过接口抽象与
|
||||
具体支付网关解耦。这一设计使得新增支付渠道时,仅需实现相应策略类,
|
||||
无需修改核心订单逻辑,体现了开闭原则的应用。
|
||||
|
||||
从依赖方向分析,系统呈现清晰的单向依赖:表现层依赖业务层,业务层
|
||||
依赖数据层,未发现循环依赖。这一架构特征确保了模块的独立可测试性,
|
||||
同时为后续微服务拆分奠定了基础。
|
||||
```
|
||||
|
||||
## 写作策略
|
||||
|
||||
### 策略一:主语转换
|
||||
|
||||
将主语从开发者视角转移到系统/代码本身:
|
||||
|
||||
| 禁止 | 推荐 |
|
||||
|------|------|
|
||||
| 我们设计了... | 系统采用... |
|
||||
| 开发者实现了... | 该模块通过... |
|
||||
| 代码中使用了... | 架构设计体现了... |
|
||||
|
||||
### 策略二:逻辑连接
|
||||
|
||||
使用连接词确保段落递进:
|
||||
|
||||
- **承接**:此外、进一步、在此基础上
|
||||
- **转折**:然而、值得注意的是、不同于
|
||||
- **因果**:因此、这一设计使得、由此可见
|
||||
- **总结**:综上所述、从整体来看、概言之
|
||||
|
||||
### 策略三:深度阐释
|
||||
|
||||
每个技术点需包含:
|
||||
1. **是什么**:客观描述技术实现
|
||||
2. **为什么**:阐释设计意图和考量
|
||||
3. **影响**:说明对系统的影响和价值
|
||||
|
||||
```markdown
|
||||
<!-- 示例 -->
|
||||
系统采用依赖注入模式管理组件生命周期(是什么)。这一选择源于
|
||||
对可测试性和松耦合的追求(为什么)。通过将依赖关系外置于
|
||||
配置层,各模块可独立进行单元测试,同时为运行时替换实现
|
||||
提供了可能(影响)。
|
||||
```
|
||||
|
||||
## 章节模板
|
||||
|
||||
### 架构概述(段落式)
|
||||
|
||||
```markdown
|
||||
## 系统架构概述
|
||||
|
||||
{项目名称}采用{架构模式}架构,整体设计围绕{核心理念}展开。
|
||||
从宏观视角审视,系统可划分为{N}个主要层次,各层职责明确,
|
||||
边界清晰。
|
||||
|
||||
{表现层/入口层}作为系统与外部交互的唯一入口,承担请求解析、
|
||||
参数校验、响应封装等职责。该层通过{框架/技术}实现,遵循
|
||||
{设计原则},确保接口的一致性与可维护性。
|
||||
|
||||
{业务层}是系统的核心所在,封装了全部业务逻辑。该层采用
|
||||
{模式/策略}组织代码,将复杂业务拆解为{N}个领域模块。
|
||||
值得注意的是,{关键设计决策}体现了对{质量属性}的重视。
|
||||
|
||||
{数据层}负责持久化与数据访问,通过{技术/框架}实现。
|
||||
该层与业务层通过{接口/抽象}解耦,使得数据源的替换
|
||||
不影响上层逻辑,体现了依赖倒置原则的应用。
|
||||
```
|
||||
|
||||
### 设计模式分析(段落式)
|
||||
|
||||
```markdown
|
||||
## 设计模式应用
|
||||
|
||||
代码库中可识别出{模式1}、{模式2}等设计模式的应用,
|
||||
这些模式的选择与系统的{核心需求}密切相关。
|
||||
|
||||
{模式1}主要应用于{场景/模块}。具体实现位于
|
||||
`{文件路径}`,通过{实现方式}达成{目标}。
|
||||
这一模式的引入有效解决了{问题},使得{效果}。
|
||||
|
||||
在{另一场景}中,系统采用{模式2}应对{挑战}。
|
||||
不同于{模式1}的{特点},{模式2}更侧重于{关注点}。
|
||||
从`{文件路径}`的实现可以看出,设计者通过
|
||||
{具体实现}实现了{目标}。
|
||||
|
||||
综合来看,模式的选择体现了对{原则}的遵循,
|
||||
为系统的{质量属性}提供了有力支撑。
|
||||
```
|
||||
|
||||
### 算法流程分析(段落式)
|
||||
|
||||
```markdown
|
||||
## 核心算法设计
|
||||
|
||||
{算法名称}是系统处理{业务场景}的核心逻辑,
|
||||
其实现位于`{文件路径}`。
|
||||
|
||||
从算法流程来看,整体可分为{N}个阶段。首先,
|
||||
{第一阶段描述},这一步骤的目的在于{目的}。
|
||||
随后,算法进入{第二阶段},通过{方法}实现{目标}。
|
||||
最终,{结果处理}完成整个处理流程。
|
||||
|
||||
在复杂度方面,该算法的时间复杂度为{O(x)},
|
||||
空间复杂度为{O(y)}。这一复杂度特征源于
|
||||
{原因},在{数据规模}场景下表现良好。
|
||||
|
||||
值得关注的是,{算法名称}采用了{优化策略},
|
||||
相较于朴素实现,{具体优化点}。这一设计决策
|
||||
使得{性能提升/效果}。
|
||||
```
|
||||
|
||||
## 质量检查清单
|
||||
|
||||
- [ ] 无清单罗列(禁止 `-` 或 `|` 表格作为主体内容)
|
||||
- [ ] 段落完整(每段 3-5 句,逻辑闭环)
|
||||
- [ ] 逻辑递进(有连接词串联)
|
||||
- [ ] 客观表达(无"我们"、"开发者"等主观主语)
|
||||
- [ ] 深度阐释(包含是什么/为什么/影响)
|
||||
- [ ] 代码引用(关键点附文件路径)
|
||||
@@ -1,10 +1,17 @@
|
||||
# Analysis Mode Protocol
|
||||
|
||||
## Mode Definition
|
||||
|
||||
**Mode**: `analysis` (READ-ONLY)
|
||||
**Tools**: Gemini, Qwen (default mode)
|
||||
## Prompt Structure
|
||||
|
||||
```
|
||||
PURPOSE: [development goal]
|
||||
TASK: [specific implementation task]
|
||||
MODE: [auto|write]
|
||||
CONTEXT: [file patterns]
|
||||
EXPECTED: [deliverables]
|
||||
RULES: [templates | additional constraints]
|
||||
```
|
||||
## Operation Boundaries
|
||||
|
||||
### ALLOWED Operations
|
||||
@@ -27,8 +34,8 @@
|
||||
2. **Read** and analyze CONTEXT files thoroughly
|
||||
3. **Identify** patterns, issues, and dependencies
|
||||
4. **Generate** insights and recommendations
|
||||
5. **Output** structured analysis (text response only)
|
||||
6. **Validate** EXPECTED deliverables met
|
||||
5. **Validate** EXPECTED deliverables met
|
||||
6. **Output** structured analysis (text response only)
|
||||
|
||||
## Core Requirements
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# Write Mode Protocol
|
||||
## Prompt Structure
|
||||
|
||||
## Mode Definition
|
||||
|
||||
**Mode**: `write` (FILE OPERATIONS) / `auto` (FULL OPERATIONS)
|
||||
**Tools**: Codex (auto), Gemini/Qwen (write)
|
||||
|
||||
```
|
||||
PURPOSE: [development goal]
|
||||
TASK: [specific implementation task]
|
||||
MODE: [auto|write]
|
||||
CONTEXT: [file patterns]
|
||||
EXPECTED: [deliverables]
|
||||
RULES: [templates | additional constraints]
|
||||
```
|
||||
## Operation Boundaries
|
||||
|
||||
### MODE: write
|
||||
@@ -15,12 +19,6 @@
|
||||
|
||||
**Restrictions**: Follow project conventions, cannot break existing functionality
|
||||
|
||||
### MODE: auto (Codex only)
|
||||
- All `write` mode operations
|
||||
- Run tests and builds
|
||||
- Commit code incrementally
|
||||
- Full autonomous development
|
||||
|
||||
**Constraint**: Must test every change
|
||||
|
||||
## Execution Flow
|
||||
@@ -33,16 +31,6 @@
|
||||
5. **Validate** changes
|
||||
6. **Report** file changes
|
||||
|
||||
### MODE: auto
|
||||
1. **Parse** all 6 fields
|
||||
2. **Analyze** CONTEXT files - find 3+ similar patterns
|
||||
3. **Plan** implementation following RULES
|
||||
4. **Generate** code with tests
|
||||
5. **Run** tests continuously
|
||||
6. **Commit** working code incrementally
|
||||
7. **Validate** EXPECTED deliverables
|
||||
8. **Report** results
|
||||
|
||||
## Core Requirements
|
||||
|
||||
**ALWAYS**:
|
||||
@@ -61,17 +49,6 @@
|
||||
- Break backward compatibility
|
||||
- Exceed 3 failed attempts without stopping
|
||||
|
||||
## Multi-Task Execution (Resume)
|
||||
|
||||
**First subtask**: Standard execution flow
|
||||
**Subsequent subtasks** (via `resume`):
|
||||
- Recall context from previous subtasks
|
||||
- Build on previous work
|
||||
- Maintain consistency
|
||||
- Test integration
|
||||
- Report context for next subtask
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Three-Attempt Rule**: On 3rd failure, stop and report what attempted, what failed, root cause
|
||||
|
||||
@@ -92,7 +69,7 @@
|
||||
|
||||
**If template has no format** → Use default format below
|
||||
|
||||
### Single Task Implementation
|
||||
### Task Implementation
|
||||
|
||||
```markdown
|
||||
# Implementation: [TASK Title]
|
||||
@@ -124,48 +101,6 @@
|
||||
[Recommendations if any]
|
||||
```
|
||||
|
||||
### Multi-Task (First Subtask)
|
||||
|
||||
```markdown
|
||||
# Subtask 1/N: [TASK Title]
|
||||
|
||||
## Changes
|
||||
[List of file changes]
|
||||
|
||||
## Implementation
|
||||
[Details with code references]
|
||||
|
||||
## Testing
|
||||
✅ Tests: X passing
|
||||
|
||||
## Context for Next Subtask
|
||||
- Key decisions: [established patterns]
|
||||
- Files created: [paths and purposes]
|
||||
- Integration points: [where next subtask should connect]
|
||||
```
|
||||
|
||||
### Multi-Task (Subsequent Subtasks)
|
||||
|
||||
```markdown
|
||||
# Subtask N/M: [TASK Title]
|
||||
|
||||
## Changes
|
||||
[List of file changes]
|
||||
|
||||
## Integration Notes
|
||||
✅ Compatible with previous subtask
|
||||
✅ Maintains established patterns
|
||||
|
||||
## Implementation
|
||||
[Details with code references]
|
||||
|
||||
## Testing
|
||||
✅ Tests: X passing
|
||||
|
||||
## Context for Next Subtask
|
||||
[If not final, provide context]
|
||||
```
|
||||
|
||||
### Partial Completion
|
||||
|
||||
```markdown
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Conflict Resolution Schema",
|
||||
"description": "Simplified schema for conflict detection and resolution",
|
||||
"description": "Schema for conflict detection, strategy generation, and resolution output",
|
||||
|
||||
"type": "object",
|
||||
"required": ["conflicts", "summary"],
|
||||
@@ -10,7 +10,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "brief", "severity", "category", "strategies"],
|
||||
"required": ["id", "brief", "severity", "category", "strategies", "recommended"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
@@ -38,10 +38,41 @@
|
||||
"type": "string",
|
||||
"description": "详细冲突描述"
|
||||
},
|
||||
"clarification_questions": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "需要用户澄清的问题(可选)"
|
||||
"impact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": { "type": "string", "description": "影响的模块/组件" },
|
||||
"compatibility": { "enum": ["Yes", "No", "Partial"] },
|
||||
"migration_required": { "type": "boolean" },
|
||||
"estimated_effort": { "type": "string", "description": "人天估计" }
|
||||
}
|
||||
},
|
||||
"overlap_analysis": {
|
||||
"type": "object",
|
||||
"description": "仅当 category=ModuleOverlap 时需要",
|
||||
"properties": {
|
||||
"new_module": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"scenarios": { "type": "array", "items": { "type": "string" } },
|
||||
"responsibilities": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"existing_modules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"scenarios": { "type": "array", "items": { "type": "string" } },
|
||||
"overlap_scenarios": { "type": "array", "items": { "type": "string" } },
|
||||
"responsibilities": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"strategies": {
|
||||
"type": "array",
|
||||
@@ -49,26 +80,34 @@
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "approach", "complexity", "risk"],
|
||||
"required": ["name", "approach", "complexity", "risk", "effort", "pros", "cons"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "策略名称(中文)"
|
||||
},
|
||||
"approach": {
|
||||
"type": "string",
|
||||
"description": "实现方法简述"
|
||||
},
|
||||
"complexity": {
|
||||
"enum": ["Low", "Medium", "High"]
|
||||
},
|
||||
"risk": {
|
||||
"enum": ["Low", "Medium", "High"]
|
||||
},
|
||||
"constraints": {
|
||||
"name": { "type": "string", "description": "策略名称(中文)" },
|
||||
"approach": { "type": "string", "description": "实现方法简述" },
|
||||
"complexity": { "enum": ["Low", "Medium", "High"] },
|
||||
"risk": { "enum": ["Low", "Medium", "High"] },
|
||||
"effort": { "type": "string", "description": "时间估计" },
|
||||
"pros": { "type": "array", "items": { "type": "string" }, "description": "优点" },
|
||||
"cons": { "type": "array", "items": { "type": "string" }, "description": "缺点" },
|
||||
"clarification_needed": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "实施此策略的约束条件(传递给 task-generate)"
|
||||
"description": "需要用户澄清的问题(尤其是 ModuleOverlap)"
|
||||
},
|
||||
"modifications": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["file", "section", "change_type", "old_content", "new_content", "rationale"],
|
||||
"properties": {
|
||||
"file": { "type": "string", "description": "相对项目根目录的完整路径" },
|
||||
"section": { "type": "string", "description": "Markdown heading 用于定位" },
|
||||
"change_type": { "enum": ["update", "add", "remove"] },
|
||||
"old_content": { "type": "string", "description": "原始内容片段(20-100字符,用于唯一匹配)" },
|
||||
"new_content": { "type": "string", "description": "修改后的内容" },
|
||||
"rationale": { "type": "string", "description": "修改理由" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,13 +116,20 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "推荐策略索引(0-based)"
|
||||
},
|
||||
"modification_suggestions": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"maxItems": 5,
|
||||
"items": { "type": "string" },
|
||||
"description": "自定义处理建议(2-5条,中文)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"required": ["total"],
|
||||
"required": ["total", "critical", "high", "medium"],
|
||||
"properties": {
|
||||
"total": { "type": "integer" },
|
||||
"critical": { "type": "integer" },
|
||||
@@ -93,45 +139,13 @@
|
||||
}
|
||||
},
|
||||
|
||||
"examples": [
|
||||
{
|
||||
"conflicts": [
|
||||
{
|
||||
"id": "CON-001",
|
||||
"brief": "新认证模块与现有 AuthManager 功能重叠",
|
||||
"severity": "High",
|
||||
"category": "ModuleOverlap",
|
||||
"affected_files": ["src/auth/AuthManager.ts"],
|
||||
"description": "计划新增的 UserAuthService 与现有 AuthManager 在登录和 Token 验证场景存在重叠",
|
||||
"clarification_questions": [
|
||||
"新模块的核心职责边界是什么?",
|
||||
"哪些场景应该由新模块独立处理?"
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"name": "扩展现有模块",
|
||||
"approach": "在 AuthManager 中添加新功能",
|
||||
"complexity": "Low",
|
||||
"risk": "Low",
|
||||
"constraints": ["保持 AuthManager 作为唯一认证入口", "新增 MFA 方法"]
|
||||
},
|
||||
{
|
||||
"name": "职责拆分",
|
||||
"approach": "AuthManager 负责基础认证,新模块负责高级认证",
|
||||
"complexity": "Medium",
|
||||
"risk": "Medium",
|
||||
"constraints": ["定义清晰的接口边界", "基础认证 = 密码+token", "高级认证 = MFA+OAuth"]
|
||||
}
|
||||
],
|
||||
"recommended": 0
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total": 1,
|
||||
"critical": 0,
|
||||
"high": 1,
|
||||
"medium": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
"_quality_standards": {
|
||||
"modifications": [
|
||||
"old_content: 20-100字符,确保 Edit 工具能唯一匹配",
|
||||
"new_content: 保持 markdown 格式",
|
||||
"change_type: update(替换), add(插入), remove(删除)"
|
||||
],
|
||||
"user_facing_text": "brief, name, pros, cons, modification_suggestions 使用中文",
|
||||
"technical_fields": "severity, category, complexity, risk 使用英文"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Issue Task JSONL Schema",
|
||||
"description": "Schema for individual task entries in tasks.jsonl file",
|
||||
"type": "object",
|
||||
"required": ["id", "title", "type", "description", "depends_on", "delivery_criteria", "status", "current_phase", "executor"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique task identifier (e.g., TASK-001)",
|
||||
"pattern": "^TASK-[0-9]+$"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Short summary of the task",
|
||||
"maxLength": 100
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["feature", "bug", "refactor", "test", "chore", "docs"],
|
||||
"description": "Task category"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Detailed instructions for the task"
|
||||
},
|
||||
"file_context": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "List of relevant files/globs",
|
||||
"default": []
|
||||
},
|
||||
"depends_on": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Array of Task IDs that must complete first",
|
||||
"default": []
|
||||
},
|
||||
"delivery_criteria": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Checklist items that define task completion",
|
||||
"minItems": 1
|
||||
},
|
||||
"pause_criteria": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Conditions that should halt execution (e.g., 'API spec unclear')",
|
||||
"default": []
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "ready", "in_progress", "completed", "failed", "paused", "skipped"],
|
||||
"description": "Current task status",
|
||||
"default": "pending"
|
||||
},
|
||||
"current_phase": {
|
||||
"type": "string",
|
||||
"enum": ["analyze", "implement", "test", "optimize", "commit", "done"],
|
||||
"description": "Current execution phase within the task lifecycle",
|
||||
"default": "analyze"
|
||||
},
|
||||
"executor": {
|
||||
"type": "string",
|
||||
"enum": ["agent", "codex", "gemini", "auto"],
|
||||
"description": "Preferred executor for this task",
|
||||
"default": "auto"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 5,
|
||||
"description": "Task priority (1=highest, 5=lowest)",
|
||||
"default": 3
|
||||
},
|
||||
"phase_results": {
|
||||
"type": "object",
|
||||
"description": "Results from each execution phase",
|
||||
"properties": {
|
||||
"analyze": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"findings": { "type": "array", "items": { "type": "string" } },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"implement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"files_modified": { "type": "array", "items": { "type": "string" } },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"test_results": { "type": "string" },
|
||||
"retry_count": { "type": "integer" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"optimize": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"improvements": { "type": "array", "items": { "type": "string" } },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"commit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"commit_hash": { "type": "string" },
|
||||
"message": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Task creation timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Last update timestamp"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Issues JSONL Schema",
|
||||
"description": "Schema for each line in issues.jsonl (flat storage)",
|
||||
"type": "object",
|
||||
"required": ["id", "title", "status", "created_at"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Issue ID (e.g., GH-123, TEXT-xxx)"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["registered", "planning", "planned", "queued", "executing", "completed", "failed", "paused"],
|
||||
"default": "registered"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 5,
|
||||
"default": 3
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "Issue context/description (markdown)"
|
||||
},
|
||||
"bound_solution_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the bound solution (null if none bound)"
|
||||
},
|
||||
"solution_count": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Number of candidate solutions in solutions/{id}.jsonl"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": ["github", "text", "file"],
|
||||
"description": "Source of the issue"
|
||||
},
|
||||
"source_url": {
|
||||
"type": "string",
|
||||
"description": "Original source URL (for GitHub issues)"
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Issue labels/tags"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"planned_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"queued_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"completed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
}
|
||||
136
.claude/workflows/cli-templates/schemas/queue-schema.json
Normal file
136
.claude/workflows/cli-templates/schemas/queue-schema.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Issue Execution Queue Schema",
|
||||
"description": "Global execution queue for all issue tasks",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"queue": {
|
||||
"type": "array",
|
||||
"description": "Ordered list of tasks to execute",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["queue_id", "issue_id", "solution_id", "task_id", "status"],
|
||||
"properties": {
|
||||
"queue_id": {
|
||||
"type": "string",
|
||||
"pattern": "^Q-[0-9]+$",
|
||||
"description": "Unique queue item identifier"
|
||||
},
|
||||
"issue_id": {
|
||||
"type": "string",
|
||||
"description": "Source issue ID"
|
||||
},
|
||||
"solution_id": {
|
||||
"type": "string",
|
||||
"description": "Source solution ID"
|
||||
},
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"description": "Task ID within solution"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "ready", "executing", "completed", "failed", "blocked"],
|
||||
"default": "pending"
|
||||
},
|
||||
"execution_order": {
|
||||
"type": "integer",
|
||||
"description": "Order in execution sequence"
|
||||
},
|
||||
"execution_group": {
|
||||
"type": "string",
|
||||
"description": "Parallel execution group ID (e.g., P1, S1)"
|
||||
},
|
||||
"depends_on": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Queue IDs this task depends on"
|
||||
},
|
||||
"semantic_priority": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Semantic importance score (0.0-1.0)"
|
||||
},
|
||||
"assigned_executor": {
|
||||
"type": "string",
|
||||
"enum": ["codex", "gemini", "agent"]
|
||||
},
|
||||
"queued_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"started_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"completed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"result": {
|
||||
"type": "object",
|
||||
"description": "Execution result",
|
||||
"properties": {
|
||||
"files_modified": { "type": "array", "items": { "type": "string" } },
|
||||
"files_created": { "type": "array", "items": { "type": "string" } },
|
||||
"summary": { "type": "string" },
|
||||
"commit_hash": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"failure_reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"conflicts": {
|
||||
"type": "array",
|
||||
"description": "Detected conflicts between tasks",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["file_conflict", "dependency_conflict", "resource_conflict"]
|
||||
},
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Queue IDs involved in conflict"
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "Conflicting file path"
|
||||
},
|
||||
"resolution": {
|
||||
"type": "string",
|
||||
"enum": ["sequential", "merge", "manual"]
|
||||
},
|
||||
"resolution_order": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"resolved": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"_metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": { "type": "string", "default": "1.0" },
|
||||
"total_items": { "type": "integer" },
|
||||
"pending_count": { "type": "integer" },
|
||||
"ready_count": { "type": "integer" },
|
||||
"executing_count": { "type": "integer" },
|
||||
"completed_count": { "type": "integer" },
|
||||
"failed_count": { "type": "integer" },
|
||||
"last_queue_formation": { "type": "string", "format": "date-time" },
|
||||
"last_updated": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
.claude/workflows/cli-templates/schemas/registry-schema.json
Normal file
94
.claude/workflows/cli-templates/schemas/registry-schema.json
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Issue Registry Schema",
|
||||
"description": "Global registry of all issues and their solutions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "array",
|
||||
"description": "List of registered issues",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "title", "status", "created_at"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Issue ID (e.g., GH-123, TEXT-xxx)"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["registered", "planning", "planned", "queued", "executing", "completed", "failed", "paused"],
|
||||
"default": "registered"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 5,
|
||||
"default": 3
|
||||
},
|
||||
"solution_count": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Number of candidate solutions"
|
||||
},
|
||||
"bound_solution_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the bound solution (null if none bound)"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": ["github", "text", "file"],
|
||||
"description": "Source of the issue"
|
||||
},
|
||||
"source_url": {
|
||||
"type": "string",
|
||||
"description": "Original source URL (for GitHub issues)"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"planned_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"queued_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"completed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"_metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": { "type": "string", "default": "1.0" },
|
||||
"total_issues": { "type": "integer" },
|
||||
"by_status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"registered": { "type": "integer" },
|
||||
"planning": { "type": "integer" },
|
||||
"planned": { "type": "integer" },
|
||||
"queued": { "type": "integer" },
|
||||
"executing": { "type": "integer" },
|
||||
"completed": { "type": "integer" },
|
||||
"failed": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"last_updated": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
.claude/workflows/cli-templates/schemas/solution-schema.json
Normal file
120
.claude/workflows/cli-templates/schemas/solution-schema.json
Normal file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Issue Solution Schema",
|
||||
"description": "Schema for solution registered to an issue",
|
||||
"type": "object",
|
||||
"required": ["id", "issue_id", "tasks", "status", "created_at"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique solution identifier",
|
||||
"pattern": "^SOL-[0-9]+$"
|
||||
},
|
||||
"issue_id": {
|
||||
"type": "string",
|
||||
"description": "Parent issue ID"
|
||||
},
|
||||
"plan_session_id": {
|
||||
"type": "string",
|
||||
"description": "Planning session that created this solution"
|
||||
},
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"description": "Task breakdown for this solution",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "title", "scope", "action", "acceptance"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^T[0-9]+$"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Action verb + target"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "Module path or feature area"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": ["Create", "Update", "Implement", "Refactor", "Add", "Delete", "Configure", "Test", "Fix"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "1-2 sentences describing what to implement"
|
||||
},
|
||||
"modification_points": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file": { "type": "string" },
|
||||
"target": { "type": "string" },
|
||||
"change": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"implementation": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Step-by-step implementation guide"
|
||||
},
|
||||
"acceptance": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Quantified completion criteria"
|
||||
},
|
||||
"depends_on": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": [],
|
||||
"description": "Task IDs this task depends on"
|
||||
},
|
||||
"estimated_minutes": {
|
||||
"type": "integer",
|
||||
"description": "Estimated time to complete"
|
||||
},
|
||||
"executor": {
|
||||
"type": "string",
|
||||
"enum": ["codex", "gemini", "agent", "auto"],
|
||||
"default": "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exploration_context": {
|
||||
"type": "object",
|
||||
"description": "ACE exploration results",
|
||||
"properties": {
|
||||
"project_structure": { "type": "string" },
|
||||
"relevant_files": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"patterns": { "type": "string" },
|
||||
"integration_points": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["draft", "candidate", "bound", "queued", "executing", "completed", "failed"],
|
||||
"default": "draft"
|
||||
},
|
||||
"is_bound": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether this solution is bound to the issue"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"bound_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When this solution was bound to the issue"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Solutions JSONL Schema",
|
||||
"description": "Schema for each line in solutions/{issue-id}.jsonl",
|
||||
"type": "object",
|
||||
"required": ["id", "tasks", "created_at"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique solution identifier",
|
||||
"pattern": "^SOL-[0-9]+$"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Solution approach description"
|
||||
},
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"description": "Task breakdown for this solution",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "title", "scope", "action", "acceptance"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^T[0-9]+$"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Action verb + target"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "Module path or feature area"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": ["Create", "Update", "Implement", "Refactor", "Add", "Delete", "Configure", "Test", "Fix"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "1-2 sentences describing what to implement"
|
||||
},
|
||||
"modification_points": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file": { "type": "string" },
|
||||
"target": { "type": "string" },
|
||||
"change": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"implementation": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Step-by-step implementation guide"
|
||||
},
|
||||
"acceptance": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Quantified completion criteria"
|
||||
},
|
||||
"depends_on": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": [],
|
||||
"description": "Task IDs this task depends on"
|
||||
},
|
||||
"estimated_minutes": {
|
||||
"type": "integer",
|
||||
"description": "Estimated time to complete"
|
||||
},
|
||||
"executor": {
|
||||
"type": "string",
|
||||
"enum": ["codex", "gemini", "agent", "auto"],
|
||||
"default": "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exploration_context": {
|
||||
"type": "object",
|
||||
"description": "ACE exploration results",
|
||||
"properties": {
|
||||
"project_structure": { "type": "string" },
|
||||
"relevant_files": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"patterns": { "type": "string" },
|
||||
"integration_points": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"analysis": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"risk": { "type": "string", "enum": ["low", "medium", "high"] },
|
||||
"impact": { "type": "string", "enum": ["low", "medium", "high"] },
|
||||
"complexity": { "type": "string", "enum": ["low", "medium", "high"] }
|
||||
}
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Solution quality score (0.0-1.0)"
|
||||
},
|
||||
"is_bound": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether this solution is bound to the issue"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"bound_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When this solution was bound to the issue"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,13 +65,13 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/[mode]-protocol.md) $(c
|
||||
ccw cli -p "<PROMPT>" --tool <gemini|qwen|codex> --mode <analysis|write>
|
||||
```
|
||||
|
||||
**⚠️ CRITICAL**: `--mode` parameter is **MANDATORY** for all CLI executions. No defaults are assumed.
|
||||
**Note**: `--mode` defaults to `analysis` if not specified. Explicitly specify `--mode write` for file operations.
|
||||
|
||||
### Core Principles
|
||||
|
||||
- **Use tools early and often** - Tools are faster and more thorough
|
||||
- **Unified CLI** - Always use `ccw cli -p` for consistent parameter handling
|
||||
- **Mode is MANDATORY** - ALWAYS explicitly specify `--mode analysis|write` (no implicit defaults)
|
||||
- **Default mode is analysis** - Omit `--mode` for read-only operations, explicitly use `--mode write` for file modifications
|
||||
- **One template required** - ALWAYS reference exactly ONE template in RULES (use universal fallback if no specific match)
|
||||
- **Write protection** - Require EXPLICIT `--mode write` for file operations
|
||||
- **Use double quotes for shell expansion** - Always wrap prompts in double quotes `"..."` to enable `$(cat ...)` command substitution; NEVER use single quotes or escape characters (`\$`, `\"`, `\'`)
|
||||
@@ -183,7 +183,6 @@ ASSISTANT RESPONSE: [Previous output]
|
||||
|
||||
**Tool Behavior**: Codex uses native `codex resume`; Gemini/Qwen assembles context as single prompt
|
||||
|
||||
---
|
||||
|
||||
## Prompt Template
|
||||
|
||||
@@ -362,10 +361,6 @@ ccw cli -p "RULES: \$(cat ~/.claude/workflows/cli-templates/protocols/analysis-p
|
||||
- Description: Additional directories (comma-separated)
|
||||
- Default: none
|
||||
|
||||
- **`--timeout <ms>`**
|
||||
- Description: Timeout in milliseconds
|
||||
- Default: 300000
|
||||
|
||||
- **`--resume [id]`**
|
||||
- Description: Resume previous session
|
||||
- Default: -
|
||||
@@ -430,7 +425,7 @@ MODE: analysis
|
||||
CONTEXT: @src/auth/**/* @src/middleware/auth.ts | Memory: Using bcrypt for passwords, JWT for sessions
|
||||
EXPECTED: Security report with: severity matrix, file:line references, CVE mappings where applicable, remediation code snippets prioritized by risk
|
||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) $(cat ~/.claude/workflows/cli-templates/prompts/analysis/03-assess-security-risks.txt) | Focus on authentication | Ignore test files
|
||||
" --tool gemini --cd src/auth --timeout 600000
|
||||
" --tool gemini --mode analysis --cd src/auth
|
||||
```
|
||||
|
||||
**Implementation Task** (New Feature):
|
||||
@@ -442,7 +437,7 @@ MODE: write
|
||||
CONTEXT: @src/middleware/**/* @src/config/**/* | Memory: Using Express.js, Redis already configured, existing middleware pattern in auth.ts
|
||||
EXPECTED: Production-ready code with: TypeScript types, unit tests, integration test, configuration example, migration guide
|
||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) $(cat ~/.claude/workflows/cli-templates/prompts/development/02-implement-feature.txt) | Follow existing middleware patterns | No breaking changes
|
||||
" --tool codex --mode write --timeout 1800000
|
||||
" --tool codex --mode write
|
||||
```
|
||||
|
||||
**Bug Fix Task**:
|
||||
@@ -454,7 +449,7 @@ MODE: analysis
|
||||
CONTEXT: @src/websocket/**/* @src/services/connection-manager.ts | Memory: Using ws library, ~5000 concurrent connections in production
|
||||
EXPECTED: Root cause analysis with: memory profile, leak source (file:line), fix recommendation with code, verification steps
|
||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) $(cat ~/.claude/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt) | Focus on resource cleanup
|
||||
" --tool gemini --cd src --timeout 900000
|
||||
" --tool gemini --mode analysis --cd src
|
||||
```
|
||||
|
||||
**Refactoring Task**:
|
||||
@@ -466,30 +461,25 @@ MODE: write
|
||||
CONTEXT: @src/payments/**/* @src/types/payment.ts | Memory: Currently only Stripe, adding PayPal next sprint, must support future gateways
|
||||
EXPECTED: Refactored code with: strategy interface, concrete implementations, factory class, updated tests, migration checklist
|
||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) $(cat ~/.claude/workflows/cli-templates/prompts/development/02-refactor-codebase.txt) | Preserve all existing behavior | Tests must pass
|
||||
" --tool gemini --mode write --timeout 1200000
|
||||
" --tool gemini --mode write
|
||||
```
|
||||
---
|
||||
|
||||
## Configuration
|
||||
## ⚙️ Execution Configuration
|
||||
|
||||
### Timeout Allocation
|
||||
### Dynamic Timeout Allocation
|
||||
|
||||
**Minimum**: 5 minutes (300000ms)
|
||||
**Minimum timeout: 5 minutes (300000ms)** - Never set below this threshold.
|
||||
|
||||
- **Simple**: 5-10min (300000-600000ms)
|
||||
- Examples: Analysis, search
|
||||
**Timeout Ranges**:
|
||||
- **Simple** (analysis, search): 5-10min (300000-600000ms)
|
||||
- **Medium** (refactoring, documentation): 10-20min (600000-1200000ms)
|
||||
- **Complex** (implementation, migration): 20-60min (1200000-3600000ms)
|
||||
- **Heavy** (large codebase, multi-file): 60-120min (3600000-7200000ms)
|
||||
|
||||
- **Medium**: 10-20min (600000-1200000ms)
|
||||
- Examples: Refactoring, documentation
|
||||
|
||||
- **Complex**: 20-60min (1200000-3600000ms)
|
||||
- Examples: Implementation, migration
|
||||
|
||||
- **Heavy**: 60-120min (3600000-7200000ms)
|
||||
- Examples: Large codebase, multi-file
|
||||
|
||||
**Codex Multiplier**: 3x allocated time (minimum 15min / 900000ms)
|
||||
**Codex Multiplier**: 3x of allocated time (minimum 15min / 900000ms)
|
||||
|
||||
**Auto-detection**: Analyze PURPOSE and TASK fields to determine timeout
|
||||
|
||||
### Permission Framework
|
||||
|
||||
@@ -523,4 +513,3 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) $(ca
|
||||
- [ ] **Tool selected** - `--tool gemini|qwen|codex`
|
||||
- [ ] **Template applied (REQUIRED)** - Use specific or universal fallback template
|
||||
- [ ] **Constraints specified** - Scope, requirements
|
||||
- [ ] **Timeout configured** - Based on complexity
|
||||
|
||||
105
.claude/workflows/context-tools-ace.md
Normal file
105
.claude/workflows/context-tools-ace.md
Normal file
@@ -0,0 +1,105 @@
|
||||
## MCP Tools Usage
|
||||
|
||||
### search_context (ACE) - Code Search (REQUIRED - HIGHEST PRIORITY)
|
||||
|
||||
**OVERRIDES**: All other search/discovery rules in other workflow files
|
||||
|
||||
**When**: ANY code discovery task, including:
|
||||
- Find code, understand codebase structure, locate implementations
|
||||
- Explore unknown locations
|
||||
- Verify file existence before reading
|
||||
- Pattern-based file discovery
|
||||
- Semantic code understanding
|
||||
|
||||
**Priority Rule**:
|
||||
1. **Always use mcp__ace-tool__search_context FIRST** for any code/file discovery
|
||||
2. Only use Built-in Grep for single-file exact line search (after location confirmed)
|
||||
3. Only use Built-in Read for known, confirmed file paths
|
||||
|
||||
**How**:
|
||||
```javascript
|
||||
// Natural language code search - best for understanding and exploration
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: "/path/to/project",
|
||||
query: "authentication logic"
|
||||
})
|
||||
|
||||
// With keywords for better semantic matching
|
||||
mcp__ace-tool__search_context({
|
||||
project_root_path: "/path/to/project",
|
||||
query: "I want to find where the server handles user login. Keywords: auth, login, session"
|
||||
})
|
||||
```
|
||||
|
||||
**Good Query Examples**:
|
||||
- "Where is the function that handles user authentication?"
|
||||
- "What tests are there for the login functionality?"
|
||||
- "How is the database connected to the application?"
|
||||
- "I want to find where the server handles chunk merging. Keywords: upload chunk merge"
|
||||
- "Locate where the system refreshes cached data. Keywords: cache refresh, invalidation"
|
||||
|
||||
**Bad Query Examples** (use grep or file view instead):
|
||||
- "Find definition of constructor of class Foo" (use grep tool instead)
|
||||
- "Find all references to function bar" (use grep tool instead)
|
||||
- "Show me how Checkout class is used in services/payment.py" (use file view tool instead)
|
||||
|
||||
**Key Features**:
|
||||
- Real-time index of the codebase (always up-to-date)
|
||||
- Cross-language retrieval support
|
||||
- Semantic search with embeddings
|
||||
- No manual index initialization required
|
||||
|
||||
---
|
||||
|
||||
### read_file - Read File Contents
|
||||
|
||||
**When**: Read files found by search_context
|
||||
|
||||
**How**:
|
||||
```javascript
|
||||
read_file(path="/path/to/file.ts") // Single file
|
||||
read_file(path="/src/**/*.config.ts") // Pattern matching
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### edit_file - Modify Files
|
||||
|
||||
**When**: Built-in Edit tool fails or need advanced features
|
||||
|
||||
**How**:
|
||||
```javascript
|
||||
edit_file(path="/file.ts", old_string="...", new_string="...", mode="update")
|
||||
edit_file(path="/file.ts", line=10, content="...", mode="insert_after")
|
||||
```
|
||||
|
||||
**Modes**: `update` (replace text), `insert_after`, `insert_before`, `delete_line`
|
||||
|
||||
---
|
||||
|
||||
### write_file - Create/Overwrite Files
|
||||
|
||||
**When**: Create new files or completely replace content
|
||||
|
||||
**How**:
|
||||
```javascript
|
||||
write_file(path="/new-file.ts", content="...")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Exa - External Search
|
||||
|
||||
**When**: Find documentation/examples outside codebase
|
||||
|
||||
**How**:
|
||||
```javascript
|
||||
mcp__exa__search(query="React hooks 2025 documentation")
|
||||
mcp__exa__search(query="FastAPI auth example", numResults=10)
|
||||
mcp__exa__search(query="latest API docs", livecrawl="always")
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `query` (required): Search query string
|
||||
- `numResults` (optional): Number of results to return (default: 5)
|
||||
- `livecrawl` (optional): `"always"` or `"fallback"` for live crawling
|
||||
@@ -21,8 +21,11 @@
|
||||
- Graceful degradation
|
||||
- Don't expose sensitive info
|
||||
|
||||
|
||||
|
||||
## Core Principles
|
||||
|
||||
|
||||
**Incremental Progress**:
|
||||
- Small, testable changes
|
||||
- Commit working code frequently
|
||||
@@ -43,11 +46,58 @@
|
||||
- Maintain established patterns
|
||||
- Test integration between subtasks
|
||||
|
||||
|
||||
## System Optimization
|
||||
|
||||
**Direct Binary Calls**: Always call binaries directly in `functions.shell`, set `workdir`, avoid shell wrappers (`bash -lc`, `cmd /c`, etc.)
|
||||
|
||||
**Text Editing Priority**:
|
||||
1. Use `apply_patch` tool for all routine text edits
|
||||
2. Fall back to `sed` for single-line substitutions if unavailable
|
||||
3. Avoid Python editing scripts unless both fail
|
||||
|
||||
**apply_patch invocation**:
|
||||
```json
|
||||
{
|
||||
"command": ["apply_patch", "*** Begin Patch\n*** Update File: path/to/file\n@@\n- old\n+ new\n*** End Patch\n"],
|
||||
"workdir": "<workdir>",
|
||||
"justification": "Brief reason"
|
||||
}
|
||||
```
|
||||
|
||||
**Windows UTF-8 Encoding** (before commands):
|
||||
```powershell
|
||||
[Console]::InputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
chcp 65001 > $null
|
||||
```
|
||||
|
||||
## Context Acquisition (MCP Tools Priority)
|
||||
|
||||
**For task context gathering and analysis, ALWAYS prefer MCP tools**:
|
||||
|
||||
1. **smart_search** - First choice for code discovery
|
||||
- Use `smart_search(query="...")` for semantic/keyword search
|
||||
- Use `smart_search(action="find_files", pattern="*.ts")` for file discovery
|
||||
- Supports modes: `auto`, `hybrid`, `exact`, `ripgrep`
|
||||
|
||||
2. **read_file** - Batch file reading
|
||||
- Read multiple files in parallel: `read_file(path="file1.ts")`, `read_file(path="file2.ts")`
|
||||
- Supports glob patterns: `read_file(path="src/**/*.config.ts")`
|
||||
|
||||
**Priority Order**:
|
||||
```
|
||||
smart_search (discovery) → read_file (batch read) → shell commands (fallback)
|
||||
```
|
||||
|
||||
**NEVER** use shell commands (`cat`, `find`, `grep`) when MCP tools are available.
|
||||
|
||||
## Execution Checklist
|
||||
|
||||
**Before**:
|
||||
- [ ] Understand PURPOSE and TASK clearly
|
||||
- [ ] Review CONTEXT files, find 3+ patterns
|
||||
- [ ] Use smart_search to discover relevant files
|
||||
- [ ] Use read_file to batch read context files, find 3+ patterns
|
||||
- [ ] Check RULES templates and constraints
|
||||
|
||||
**During**:
|
||||
|
||||
Binary file not shown.
378
.codex/prompts/compact.md
Normal file
378
.codex/prompts/compact.md
Normal file
@@ -0,0 +1,378 @@
|
||||
---
|
||||
description: Compact current session memory into structured text for session recovery
|
||||
argument-hint: "[optional: session description]"
|
||||
---
|
||||
|
||||
# Memory Compact Command (/memory:compact)
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The `memory:compact` command **compresses current session working memory** into structured text optimized for **session recovery**, extracts critical information, and saves it to persistent storage via MCP `core_memory` tool.
|
||||
|
||||
**Core Philosophy**:
|
||||
- **Session Recovery First**: Capture everything needed to resume work seamlessly
|
||||
- **Minimize Re-exploration**: Include file paths, decisions, and state to avoid redundant analysis
|
||||
- **Preserve Train of Thought**: Keep notes and hypotheses for complex debugging
|
||||
- **Actionable State**: Record last action result and known issues
|
||||
|
||||
## 2. Parameters
|
||||
|
||||
- `"session description"` (Optional): Session description to supplement objective
|
||||
- Example: "completed core-memory module"
|
||||
- Example: "debugging JWT refresh - suspected memory leak"
|
||||
|
||||
## 3. Structured Output Format
|
||||
|
||||
```markdown
|
||||
## Session ID
|
||||
[WFS-ID if workflow session active, otherwise (none)]
|
||||
|
||||
## Project Root
|
||||
[Absolute path to project root, e.g., D:\Claude_dms3]
|
||||
|
||||
## Objective
|
||||
[High-level goal - the "North Star" of this session]
|
||||
|
||||
## Execution Plan
|
||||
[CRITICAL: Embed the LATEST plan in its COMPLETE and DETAILED form]
|
||||
|
||||
### Source: [workflow | todo | user-stated | inferred]
|
||||
|
||||
<details>
|
||||
<summary>Full Execution Plan (Click to expand)</summary>
|
||||
|
||||
[PRESERVE COMPLETE PLAN VERBATIM - DO NOT SUMMARIZE]
|
||||
- ALL phases, tasks, subtasks
|
||||
- ALL file paths (absolute)
|
||||
- ALL dependencies and prerequisites
|
||||
- ALL acceptance criteria
|
||||
- ALL status markers ([x] done, [ ] pending)
|
||||
- ALL notes and context
|
||||
|
||||
Example:
|
||||
## Phase 1: Setup
|
||||
- [x] Initialize project structure
|
||||
- Created D:\Claude_dms3\src\core\index.ts
|
||||
- Added dependencies: lodash, zod
|
||||
- [ ] Configure TypeScript
|
||||
- Update tsconfig.json for strict mode
|
||||
|
||||
## Phase 2: Implementation
|
||||
- [ ] Implement core API
|
||||
- Target: D:\Claude_dms3\src\api\handler.ts
|
||||
- Dependencies: Phase 1 complete
|
||||
- Acceptance: All tests pass
|
||||
|
||||
</details>
|
||||
|
||||
## Working Files (Modified)
|
||||
[Absolute paths to actively modified files]
|
||||
- D:\Claude_dms3\src\file1.ts (role: main implementation)
|
||||
- D:\Claude_dms3\tests\file1.test.ts (role: unit tests)
|
||||
|
||||
## Reference Files (Read-Only)
|
||||
[Absolute paths to context files - NOT modified but essential for understanding]
|
||||
- D:\Claude_dms3\.claude\CLAUDE.md (role: project instructions)
|
||||
- D:\Claude_dms3\src\types\index.ts (role: type definitions)
|
||||
- D:\Claude_dms3\package.json (role: dependencies)
|
||||
|
||||
## Last Action
|
||||
[Last significant action and its result/status]
|
||||
|
||||
## Decisions
|
||||
- [Decision]: [Reasoning]
|
||||
- [Decision]: [Reasoning]
|
||||
|
||||
## Constraints
|
||||
- [User-specified limitation or preference]
|
||||
|
||||
## Dependencies
|
||||
- [Added/changed packages or environment requirements]
|
||||
|
||||
## Known Issues
|
||||
- [Deferred bug or edge case]
|
||||
|
||||
## Changes Made
|
||||
- [Completed modification]
|
||||
|
||||
## Pending
|
||||
- [Next step] or (none)
|
||||
|
||||
## Notes
|
||||
[Unstructured thoughts, hypotheses, debugging trails]
|
||||
```
|
||||
|
||||
## 4. Field Definitions
|
||||
|
||||
| Field | Purpose | Recovery Value |
|
||||
|-------|---------|----------------|
|
||||
| **Session ID** | Workflow session identifier (WFS-*) | Links memory to specific stateful task execution |
|
||||
| **Project Root** | Absolute path to project directory | Enables correct path resolution in new sessions |
|
||||
| **Objective** | Ultimate goal of the session | Prevents losing track of broader feature |
|
||||
| **Execution Plan** | Complete plan from any source (verbatim) | Preserves full planning context, avoids re-planning |
|
||||
| **Working Files** | Actively modified files (absolute paths) | Immediately identifies where work was happening |
|
||||
| **Reference Files** | Read-only context files (absolute paths) | Eliminates re-exploration for critical context |
|
||||
| **Last Action** | Final tool output/status | Immediate state awareness (success/failure) |
|
||||
| **Decisions** | Architectural choices + reasoning | Prevents re-litigating settled decisions |
|
||||
| **Constraints** | User-imposed limitations | Maintains personalized coding style |
|
||||
| **Dependencies** | Package/environment changes | Prevents missing dependency errors |
|
||||
| **Known Issues** | Deferred bugs/edge cases | Ensures issues aren't forgotten |
|
||||
| **Changes Made** | Completed modifications | Clear record of what was done |
|
||||
| **Pending** | Next steps | Immediate action items |
|
||||
| **Notes** | Hypotheses, debugging trails | Preserves "train of thought" |
|
||||
|
||||
## 5. Execution Flow
|
||||
|
||||
### Step 1: Analyze Current Session
|
||||
|
||||
Extract the following from conversation history:
|
||||
|
||||
```javascript
|
||||
const sessionAnalysis = {
|
||||
sessionId: "", // WFS-* if workflow session active, null otherwise
|
||||
projectRoot: "", // Absolute path: D:\Claude_dms3
|
||||
objective: "", // High-level goal (1-2 sentences)
|
||||
executionPlan: {
|
||||
source: "workflow" | "todo" | "user-stated" | "inferred",
|
||||
content: "" // Full plan content - ALWAYS preserve COMPLETE and DETAILED form
|
||||
},
|
||||
workingFiles: [], // {absolutePath, role} - modified files
|
||||
referenceFiles: [], // {absolutePath, role} - read-only context files
|
||||
lastAction: "", // Last significant action + result
|
||||
decisions: [], // {decision, reasoning}
|
||||
constraints: [], // User-specified limitations
|
||||
dependencies: [], // Added/changed packages
|
||||
knownIssues: [], // Deferred bugs
|
||||
changesMade: [], // Completed modifications
|
||||
pending: [], // Next steps
|
||||
notes: "" // Unstructured thoughts
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Generate Structured Text
|
||||
|
||||
```javascript
|
||||
// Helper: Generate execution plan section
|
||||
const generateExecutionPlan = (plan) => {
|
||||
const sourceLabels = {
|
||||
'workflow': 'workflow (IMPL_PLAN.md)',
|
||||
'todo': 'todo (TodoWrite)',
|
||||
'user-stated': 'user-stated',
|
||||
'inferred': 'inferred'
|
||||
};
|
||||
|
||||
// CRITICAL: Preserve complete plan content verbatim - DO NOT summarize
|
||||
return `### Source: ${sourceLabels[plan.source] || plan.source}
|
||||
|
||||
<details>
|
||||
<summary>Full Execution Plan (Click to expand)</summary>
|
||||
|
||||
${plan.content}
|
||||
|
||||
</details>`;
|
||||
};
|
||||
|
||||
const structuredText = `## Session ID
|
||||
${sessionAnalysis.sessionId || '(none)'}
|
||||
|
||||
## Project Root
|
||||
${sessionAnalysis.projectRoot}
|
||||
|
||||
## Objective
|
||||
${sessionAnalysis.objective}
|
||||
|
||||
## Execution Plan
|
||||
${generateExecutionPlan(sessionAnalysis.executionPlan)}
|
||||
|
||||
## Working Files (Modified)
|
||||
${sessionAnalysis.workingFiles.map(f => `- ${f.absolutePath} (role: ${f.role})`).join('\n') || '(none)'}
|
||||
|
||||
## Reference Files (Read-Only)
|
||||
${sessionAnalysis.referenceFiles.map(f => `- ${f.absolutePath} (role: ${f.role})`).join('\n') || '(none)'}
|
||||
|
||||
## Last Action
|
||||
${sessionAnalysis.lastAction}
|
||||
|
||||
## Decisions
|
||||
${sessionAnalysis.decisions.map(d => `- ${d.decision}: ${d.reasoning}`).join('\n') || '(none)'}
|
||||
|
||||
## Constraints
|
||||
${sessionAnalysis.constraints.map(c => `- ${c}`).join('\n') || '(none)'}
|
||||
|
||||
## Dependencies
|
||||
${sessionAnalysis.dependencies.map(d => `- ${d}`).join('\n') || '(none)'}
|
||||
|
||||
## Known Issues
|
||||
${sessionAnalysis.knownIssues.map(i => `- ${i}`).join('\n') || '(none)'}
|
||||
|
||||
## Changes Made
|
||||
${sessionAnalysis.changesMade.map(c => `- ${c}`).join('\n') || '(none)'}
|
||||
|
||||
## Pending
|
||||
${sessionAnalysis.pending.length > 0
|
||||
? sessionAnalysis.pending.map(p => `- ${p}`).join('\n')
|
||||
: '(none)'}
|
||||
|
||||
## Notes
|
||||
${sessionAnalysis.notes || '(none)'}`
|
||||
```
|
||||
|
||||
### Step 3: Import to Core Memory via MCP
|
||||
|
||||
Use the MCP `core_memory` tool to save the structured text:
|
||||
|
||||
```javascript
|
||||
mcp__ccw-tools__core_memory({
|
||||
operation: "import",
|
||||
text: structuredText
|
||||
})
|
||||
```
|
||||
|
||||
Or via CLI (pipe structured text to import):
|
||||
|
||||
```bash
|
||||
# Write structured text to temp file, then import
|
||||
echo "$structuredText" | ccw core-memory import
|
||||
|
||||
# Or from a file
|
||||
ccw core-memory import --file /path/to/session-memory.md
|
||||
```
|
||||
|
||||
**Response Format**:
|
||||
```json
|
||||
{
|
||||
"operation": "import",
|
||||
"id": "CMEM-YYYYMMDD-HHMMSS",
|
||||
"message": "Created memory: CMEM-YYYYMMDD-HHMMSS"
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Report Recovery ID
|
||||
|
||||
After successful import, **clearly display the Recovery ID** to the user:
|
||||
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════════════════════╗
|
||||
║ ✓ Session Memory Saved ║
|
||||
║ ║
|
||||
║ Recovery ID: CMEM-YYYYMMDD-HHMMSS ║
|
||||
║ ║
|
||||
║ To restore: "Please import memory <ID>" ║
|
||||
║ (MCP: core_memory export | CLI: ccw core-memory export --id <ID>) ║
|
||||
╚════════════════════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
## 6. Quality Checklist
|
||||
|
||||
Before generating:
|
||||
- [ ] Session ID captured if workflow session active (WFS-*)
|
||||
- [ ] Project Root is absolute path (e.g., D:\Claude_dms3)
|
||||
- [ ] Objective clearly states the "North Star" goal
|
||||
- [ ] Execution Plan: COMPLETE plan preserved VERBATIM (no summarization)
|
||||
- [ ] Plan Source: Clearly identified (workflow | todo | user-stated | inferred)
|
||||
- [ ] Plan Details: ALL phases, tasks, file paths, dependencies, status markers included
|
||||
- [ ] All file paths are ABSOLUTE (not relative)
|
||||
- [ ] Working Files: 3-8 modified files with roles
|
||||
- [ ] Reference Files: Key context files (CLAUDE.md, types, configs)
|
||||
- [ ] Last Action captures final state (success/failure)
|
||||
- [ ] Decisions include reasoning, not just choices
|
||||
- [ ] Known Issues separates deferred from forgotten bugs
|
||||
- [ ] Notes preserve debugging hypotheses if any
|
||||
|
||||
## 7. Path Resolution Rules
|
||||
|
||||
### Project Root Detection
|
||||
1. Check current working directory from environment
|
||||
2. Look for project markers: `.git/`, `package.json`, `.claude/`
|
||||
3. Use the topmost directory containing these markers
|
||||
|
||||
### Absolute Path Conversion
|
||||
```javascript
|
||||
// Convert relative to absolute
|
||||
const toAbsolutePath = (relativePath, projectRoot) => {
|
||||
if (path.isAbsolute(relativePath)) return relativePath;
|
||||
return path.join(projectRoot, relativePath);
|
||||
};
|
||||
|
||||
// Example: "src/api/auth.ts" → "D:\Claude_dms3\src\api\auth.ts"
|
||||
```
|
||||
|
||||
### Reference File Categories
|
||||
| Category | Examples | Priority |
|
||||
|----------|----------|----------|
|
||||
| Project Config | `.claude/CLAUDE.md`, `package.json`, `tsconfig.json` | High |
|
||||
| Type Definitions | `src/types/*.ts`, `*.d.ts` | High |
|
||||
| Related Modules | Parent/sibling modules with shared interfaces | Medium |
|
||||
| Test Files | Corresponding test files for modified code | Medium |
|
||||
| Documentation | `README.md`, `ARCHITECTURE.md` | Low |
|
||||
|
||||
## 8. Plan Detection (Priority Order)
|
||||
|
||||
### Priority 1: Workflow Session (IMPL_PLAN.md)
|
||||
```javascript
|
||||
// Check for active workflow session
|
||||
const manifest = await mcp__ccw-tools__session_manager({
|
||||
operation: "list",
|
||||
location: "active"
|
||||
});
|
||||
|
||||
if (manifest.sessions?.length > 0) {
|
||||
const session = manifest.sessions[0];
|
||||
const plan = await mcp__ccw-tools__session_manager({
|
||||
operation: "read",
|
||||
session_id: session.id,
|
||||
content_type: "plan"
|
||||
});
|
||||
sessionAnalysis.sessionId = session.id;
|
||||
sessionAnalysis.executionPlan.source = "workflow";
|
||||
sessionAnalysis.executionPlan.content = plan.content;
|
||||
}
|
||||
```
|
||||
|
||||
### Priority 2: TodoWrite (Current Session Todos)
|
||||
```javascript
|
||||
// Extract from conversation - look for TodoWrite tool calls
|
||||
// Preserve COMPLETE todo list with all details
|
||||
const todos = extractTodosFromConversation();
|
||||
if (todos.length > 0) {
|
||||
sessionAnalysis.executionPlan.source = "todo";
|
||||
// Format todos with full context - preserve status markers
|
||||
sessionAnalysis.executionPlan.content = todos.map(t =>
|
||||
`- [${t.status === 'completed' ? 'x' : t.status === 'in_progress' ? '>' : ' '}] ${t.content}`
|
||||
).join('\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Priority 3: User-Stated Plan
|
||||
```javascript
|
||||
// Look for explicit plan statements in user messages:
|
||||
// - "Here's my plan: 1. ... 2. ... 3. ..."
|
||||
// - "I want to: first..., then..., finally..."
|
||||
// - Numbered or bulleted lists describing steps
|
||||
const userPlan = extractUserStatedPlan();
|
||||
if (userPlan) {
|
||||
sessionAnalysis.executionPlan.source = "user-stated";
|
||||
sessionAnalysis.executionPlan.content = userPlan;
|
||||
}
|
||||
```
|
||||
|
||||
### Priority 4: Inferred Plan
|
||||
```javascript
|
||||
// If no explicit plan, infer from:
|
||||
// - Task description and breakdown discussion
|
||||
// - Sequence of actions taken
|
||||
// - Outstanding work mentioned
|
||||
const inferredPlan = inferPlanFromDiscussion();
|
||||
if (inferredPlan) {
|
||||
sessionAnalysis.executionPlan.source = "inferred";
|
||||
sessionAnalysis.executionPlan.content = inferredPlan;
|
||||
}
|
||||
```
|
||||
|
||||
## 9. Notes
|
||||
|
||||
- **Timing**: Execute at task completion or before context switch
|
||||
- **Frequency**: Once per independent task or milestone
|
||||
- **Recovery**: New session can immediately continue with full context
|
||||
- **Knowledge Graph**: Entity relationships auto-extracted for visualization
|
||||
- **Absolute Paths**: Critical for cross-session recovery on different machines
|
||||
266
.codex/prompts/issue-execute.md
Normal file
266
.codex/prompts/issue-execute.md
Normal file
@@ -0,0 +1,266 @@
|
||||
---
|
||||
description: Execute issue queue tasks sequentially with git commit after each task
|
||||
argument-hint: "[--dry-run]"
|
||||
---
|
||||
|
||||
# Issue Execute (Codex Version)
|
||||
|
||||
## Core Principle
|
||||
|
||||
**Serial Execution**: Execute tasks ONE BY ONE from the issue queue. Complete each task fully (implement → test → commit) before moving to next. Continue autonomously until ALL tasks complete or queue is empty.
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```
|
||||
INIT: Fetch first task via ccw issue next
|
||||
|
||||
WHILE task exists:
|
||||
1. Receive task JSON from ccw issue next
|
||||
2. Execute full lifecycle:
|
||||
- IMPLEMENT: Follow task.implementation steps
|
||||
- TEST: Run task.test commands
|
||||
- VERIFY: Check task.acceptance criteria
|
||||
- COMMIT: Stage files, commit with task.commit.message_template
|
||||
3. Report completion via ccw issue complete <queue_id>
|
||||
4. Fetch next task via ccw issue next
|
||||
|
||||
WHEN queue empty:
|
||||
Output final summary
|
||||
```
|
||||
|
||||
## Step 1: Fetch First Task
|
||||
|
||||
Run this command to get your first task:
|
||||
|
||||
```bash
|
||||
ccw issue next
|
||||
```
|
||||
|
||||
This returns JSON with the full task definition:
|
||||
- `queue_id`: Unique ID for queue tracking (e.g., "Q-001")
|
||||
- `issue_id`: Parent issue ID (e.g., "ISSUE-20251227-001")
|
||||
- `task`: Full task definition with implementation steps
|
||||
- `context`: Relevant files and patterns
|
||||
- `execution_hints`: Timing and executor hints
|
||||
|
||||
If response contains `{ "status": "empty" }`, all tasks are complete - skip to final summary.
|
||||
|
||||
## Step 2: Parse Task Response
|
||||
|
||||
Expected task structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"queue_id": "Q-001",
|
||||
"issue_id": "ISSUE-20251227-001",
|
||||
"solution_id": "SOL-001",
|
||||
"task": {
|
||||
"id": "T1",
|
||||
"title": "Task title",
|
||||
"scope": "src/module/",
|
||||
"action": "Create|Modify|Fix|Refactor",
|
||||
"description": "What to do",
|
||||
"modification_points": [
|
||||
{ "file": "path/to/file.ts", "target": "function name", "change": "description" }
|
||||
],
|
||||
"implementation": [
|
||||
"Step 1: Do this",
|
||||
"Step 2: Do that"
|
||||
],
|
||||
"test": {
|
||||
"commands": ["npm test -- --filter=xxx"],
|
||||
"unit": "Unit test requirements",
|
||||
"integration": "Integration test requirements (optional)"
|
||||
},
|
||||
"acceptance": [
|
||||
"Criterion 1: Must pass",
|
||||
"Criterion 2: Must verify"
|
||||
],
|
||||
"commit": {
|
||||
"message_template": "feat(scope): description"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"relevant_files": ["path/to/reference.ts"],
|
||||
"patterns": "Follow existing pattern in xxx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 3: Execute Task Lifecycle
|
||||
|
||||
### Phase A: IMPLEMENT
|
||||
|
||||
1. Read all `context.relevant_files` to understand existing patterns
|
||||
2. Follow `task.implementation` steps in order
|
||||
3. Apply changes to `task.modification_points` files
|
||||
4. Follow `context.patterns` for code style consistency
|
||||
|
||||
**Output format:**
|
||||
```
|
||||
## Implementing: [task.title]
|
||||
|
||||
**Scope**: [task.scope]
|
||||
**Action**: [task.action]
|
||||
|
||||
**Steps**:
|
||||
1. ✓ [implementation step 1]
|
||||
2. ✓ [implementation step 2]
|
||||
...
|
||||
|
||||
**Files Modified**:
|
||||
- path/to/file1.ts
|
||||
- path/to/file2.ts
|
||||
```
|
||||
|
||||
### Phase B: TEST
|
||||
|
||||
1. Run all commands in `task.test.commands`
|
||||
2. Verify unit tests pass (`task.test.unit`)
|
||||
3. Run integration tests if specified (`task.test.integration`)
|
||||
|
||||
**If tests fail**: Fix the code and re-run. Do NOT proceed until tests pass.
|
||||
|
||||
**Output format:**
|
||||
```
|
||||
## Testing: [task.title]
|
||||
|
||||
**Test Results**:
|
||||
- [x] Unit tests: PASSED
|
||||
- [x] Integration tests: PASSED (or N/A)
|
||||
```
|
||||
|
||||
### Phase C: VERIFY
|
||||
|
||||
Check all `task.acceptance` criteria are met:
|
||||
|
||||
```
|
||||
## Verifying: [task.title]
|
||||
|
||||
**Acceptance Criteria**:
|
||||
- [x] Criterion 1: Verified
|
||||
- [x] Criterion 2: Verified
|
||||
...
|
||||
|
||||
All criteria met: YES
|
||||
```
|
||||
|
||||
**If any criterion fails**: Go back to IMPLEMENT phase and fix.
|
||||
|
||||
### Phase D: COMMIT
|
||||
|
||||
After all phases pass, commit the changes:
|
||||
|
||||
```bash
|
||||
# Stage all modified files
|
||||
git add path/to/file1.ts path/to/file2.ts ...
|
||||
|
||||
# Commit with task message template
|
||||
git commit -m "$(cat <<'EOF'
|
||||
[task.commit.message_template]
|
||||
|
||||
Queue-ID: [queue_id]
|
||||
Issue-ID: [issue_id]
|
||||
Task-ID: [task.id]
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**Output format:**
|
||||
```
|
||||
## Committed: [task.title]
|
||||
|
||||
**Commit**: [commit hash]
|
||||
**Message**: [commit message]
|
||||
**Files**: N files changed
|
||||
```
|
||||
|
||||
## Step 4: Report Completion
|
||||
|
||||
After commit succeeds, report to queue system:
|
||||
|
||||
```bash
|
||||
ccw issue complete [queue_id] --result '{
|
||||
"files_modified": ["path1", "path2"],
|
||||
"tests_passed": true,
|
||||
"acceptance_passed": true,
|
||||
"committed": true,
|
||||
"commit_hash": "[actual hash]",
|
||||
"summary": "[What was accomplished]"
|
||||
}'
|
||||
```
|
||||
|
||||
**If task failed and cannot be fixed:**
|
||||
|
||||
```bash
|
||||
ccw issue fail [queue_id] --reason "Phase [X] failed: [details]"
|
||||
```
|
||||
|
||||
## Step 5: Continue to Next Task
|
||||
|
||||
Immediately fetch the next task:
|
||||
|
||||
```bash
|
||||
ccw issue next
|
||||
```
|
||||
|
||||
**Output progress:**
|
||||
```
|
||||
✓ [N/M] Completed: [queue_id] - [task.title]
|
||||
→ Fetching next task...
|
||||
```
|
||||
|
||||
**DO NOT STOP.** Return to Step 2 and continue until queue is empty.
|
||||
|
||||
## Final Summary
|
||||
|
||||
When `ccw issue next` returns `{ "status": "empty" }`:
|
||||
|
||||
```markdown
|
||||
## Issue Queue Execution Complete
|
||||
|
||||
**Total Tasks Executed**: N
|
||||
**All Commits**:
|
||||
| # | Queue ID | Task | Commit |
|
||||
|---|----------|------|--------|
|
||||
| 1 | Q-001 | Task title | abc123 |
|
||||
| 2 | Q-002 | Task title | def456 |
|
||||
|
||||
**Files Modified**:
|
||||
- path/to/file1.ts
|
||||
- path/to/file2.ts
|
||||
|
||||
**Summary**:
|
||||
[Overall what was accomplished]
|
||||
```
|
||||
|
||||
## Execution Rules
|
||||
|
||||
1. **Never stop mid-queue** - Continue until queue is empty
|
||||
2. **One task at a time** - Fully complete (including commit) before moving on
|
||||
3. **Tests MUST pass** - Do not proceed to commit if tests fail
|
||||
4. **Commit after each task** - Each task gets its own commit
|
||||
5. **Self-verify** - All acceptance criteria must pass before commit
|
||||
6. **Report accurately** - Use ccw issue complete/fail after each task
|
||||
7. **Handle failures gracefully** - If a task fails, report via ccw issue fail and continue to next
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| ccw issue next returns empty | All done - output final summary |
|
||||
| Tests fail | Fix code, re-run tests |
|
||||
| Verification fails | Go back to implement phase |
|
||||
| Git commit fails | Check staging, retry commit |
|
||||
| ccw issue complete fails | Log error, continue to next task |
|
||||
| Unrecoverable error | Call ccw issue fail, continue to next |
|
||||
|
||||
## Start Execution
|
||||
|
||||
Begin by running:
|
||||
|
||||
```bash
|
||||
ccw issue next
|
||||
```
|
||||
|
||||
Then follow the lifecycle for each task until queue is empty.
|
||||
@@ -1,25 +1,62 @@
|
||||
# Gemini Code Guidelines
|
||||
|
||||
## Code Quality Standards
|
||||
|
||||
### Code Quality
|
||||
- Follow project's existing patterns
|
||||
- Match import style and naming conventions
|
||||
- Single responsibility per function/class
|
||||
- DRY (Don't Repeat Yourself)
|
||||
- YAGNI (You Aren't Gonna Need It)
|
||||
|
||||
### Testing
|
||||
- Test all public functions
|
||||
- Test edge cases and error conditions
|
||||
- Mock external dependencies
|
||||
- Target 80%+ coverage
|
||||
|
||||
### Error Handling
|
||||
- Proper try-catch blocks
|
||||
- Clear error messages
|
||||
- Graceful degradation
|
||||
- Don't expose sensitive info
|
||||
|
||||
## Core Principles
|
||||
|
||||
**Thoroughness**:
|
||||
- Analyze ALL CONTEXT files completely
|
||||
- Check cross-file patterns and dependencies
|
||||
- Identify edge cases and quantify metrics
|
||||
**Incremental Progress**:
|
||||
- Small, testable changes
|
||||
- Commit working code frequently
|
||||
- Build on previous work (subtasks)
|
||||
|
||||
**Evidence-Based**:
|
||||
- Quote relevant code with `file:line` references
|
||||
- Link related patterns across files
|
||||
- Support all claims with concrete examples
|
||||
- Study 3+ similar patterns before implementing
|
||||
- Match project style exactly
|
||||
- Verify with existing code
|
||||
|
||||
**Actionable**:
|
||||
- Clear, specific recommendations (not vague)
|
||||
- Prioritized by impact
|
||||
- Incremental changes over big rewrites
|
||||
**Pragmatic**:
|
||||
- Boring solutions over clever code
|
||||
- Simple over complex
|
||||
- Adapt to project reality
|
||||
|
||||
**Philosophy**:
|
||||
- **Simple over complex** - Avoid over-engineering
|
||||
- **Clear over clever** - Prefer obvious solutions
|
||||
- **Learn from existing** - Reference project patterns
|
||||
- **Pragmatic over dogmatic** - Adapt to project reality
|
||||
- **Incremental progress** - Small, testable changes
|
||||
**Context Continuity** (Multi-Task):
|
||||
- Leverage resume for consistency
|
||||
- Maintain established patterns
|
||||
- Test integration between subtasks
|
||||
|
||||
## Execution Checklist
|
||||
|
||||
**Before**:
|
||||
- [ ] Understand PURPOSE and TASK clearly
|
||||
- [ ] Review CONTEXT files, find 3+ patterns
|
||||
- [ ] Check RULES templates and constraints
|
||||
|
||||
**During**:
|
||||
- [ ] Follow existing patterns exactly
|
||||
- [ ] Write tests alongside code
|
||||
- [ ] Run tests after every change
|
||||
- [ ] Commit working code incrementally
|
||||
|
||||
**After**:
|
||||
- [ ] All tests pass
|
||||
- [ ] Coverage meets target
|
||||
- [ ] Build succeeds
|
||||
- [ ] All EXPECTED deliverables met
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,3 +29,4 @@ COMMAND_TEMPLATE_ORCHESTRATOR.md
|
||||
settings.json
|
||||
*.mcp.json
|
||||
.mcp.json
|
||||
.ace-tool/
|
||||
|
||||
22
.mcp.json
22
.mcp.json
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"chrome-devtools": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"chrome-devtools-mcp@latest"
|
||||
],
|
||||
"env": {}
|
||||
},
|
||||
"ccw-tools": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"ccw-mcp"
|
||||
],
|
||||
"env": {
|
||||
"CCW_ENABLED_TOOLS": "write_file,edit_file,smart_search,core_memory"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ Install-Claude.ps1
|
||||
install-remote.ps1
|
||||
*.mcp.json
|
||||
# ccw internal files
|
||||
ccw/package.json
|
||||
ccw/node_modules/
|
||||
ccw/*.md
|
||||
|
||||
|
||||
331
AGENTS.md
Normal file
331
AGENTS.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# Codex Agent Execution Protocol
|
||||
|
||||
## Overview
|
||||
|
||||
**Role**: Autonomous development, implementation, and testing specialist
|
||||
|
||||
|
||||
## Prompt Structure
|
||||
|
||||
All prompts follow this 6-field format:
|
||||
|
||||
```
|
||||
PURPOSE: [development goal]
|
||||
TASK: [specific implementation task]
|
||||
MODE: [auto|write]
|
||||
CONTEXT: [file patterns]
|
||||
EXPECTED: [deliverables]
|
||||
RULES: [templates | additional constraints]
|
||||
```
|
||||
|
||||
**Subtask indicator**: `Subtask N of M: [title]` or `CONTINUE TO NEXT SUBTASK`
|
||||
|
||||
## MODE Definitions
|
||||
|
||||
### MODE: auto (default)
|
||||
|
||||
**Permissions**:
|
||||
- Full file operations (create/modify/delete)
|
||||
- Run tests and builds
|
||||
- Commit code incrementally
|
||||
|
||||
**Execute**:
|
||||
1. Parse PURPOSE and TASK
|
||||
2. Analyze CONTEXT files - find 3+ similar patterns
|
||||
3. Plan implementation following RULES
|
||||
4. Generate code with tests
|
||||
5. Run tests continuously
|
||||
6. Commit working code incrementally
|
||||
7. Validate EXPECTED deliverables
|
||||
8. Report results (with context for next subtask if multi-task)
|
||||
|
||||
**Constraint**: Must test every change
|
||||
|
||||
### MODE: write
|
||||
|
||||
**Permissions**:
|
||||
- Focused file operations
|
||||
- Create/modify specific files
|
||||
- Run tests for validation
|
||||
|
||||
**Execute**:
|
||||
1. Analyze CONTEXT files
|
||||
2. Make targeted changes
|
||||
3. Validate tests pass
|
||||
4. Report file changes
|
||||
|
||||
## Execution Protocol
|
||||
|
||||
### Core Requirements
|
||||
|
||||
**ALWAYS**:
|
||||
- Parse all 6 fields (PURPOSE, TASK, MODE, CONTEXT, EXPECTED, RULES)
|
||||
- Study CONTEXT files - find 3+ similar patterns before implementing
|
||||
- Apply RULES (templates + constraints) exactly
|
||||
- Test continuously after every change
|
||||
- Commit incrementally with working code
|
||||
- Match project style and patterns exactly
|
||||
- List all created/modified files at output beginning
|
||||
- Use direct binary calls (avoid shell wrappers)
|
||||
- Prefer apply_patch for text edits
|
||||
- Configure Windows UTF-8 encoding for Chinese support
|
||||
|
||||
**NEVER**:
|
||||
- Make assumptions without code verification
|
||||
- Ignore existing patterns
|
||||
- Skip tests
|
||||
- Use clever tricks over boring solutions
|
||||
- Over-engineer solutions
|
||||
- Break existing code or backward compatibility
|
||||
- Exceed 3 failed attempts without stopping
|
||||
|
||||
### RULES Processing
|
||||
|
||||
- Parse RULES field to extract template content and constraints
|
||||
- Recognize `|` as separator: `template content | additional constraints`
|
||||
- Apply ALL template guidelines as mandatory
|
||||
- Apply ALL additional constraints as mandatory
|
||||
- Treat rule violations as task failures
|
||||
|
||||
### Multi-Task Execution (Resume Pattern)
|
||||
|
||||
**First subtask**: Standard execution flow above
|
||||
**Subsequent subtasks** (via `resume --last`):
|
||||
- Recall context from previous subtasks
|
||||
- Build on previous work (don't repeat)
|
||||
- Maintain consistency with established patterns
|
||||
- Focus on current subtask scope only
|
||||
- Test integration with previous work
|
||||
- Report context for next subtask
|
||||
|
||||
## System Optimization
|
||||
|
||||
**Direct Binary Calls**: Always call binaries directly in `functions.shell`, set `workdir`, avoid shell wrappers (`bash -lc`, `cmd /c`, etc.)
|
||||
|
||||
**Text Editing Priority**:
|
||||
1. Use `apply_patch` tool for all routine text edits
|
||||
2. Fall back to `sed` for single-line substitutions if unavailable
|
||||
3. Avoid Python editing scripts unless both fail
|
||||
|
||||
**apply_patch invocation**:
|
||||
```json
|
||||
{
|
||||
"command": ["apply_patch", "*** Begin Patch\n*** Update File: path/to/file\n@@\n- old\n+ new\n*** End Patch\n"],
|
||||
"workdir": "<workdir>",
|
||||
"justification": "Brief reason"
|
||||
}
|
||||
```
|
||||
|
||||
**Windows UTF-8 Encoding** (before commands):
|
||||
```powershell
|
||||
[Console]::InputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
chcp 65001 > $null
|
||||
```
|
||||
|
||||
## Output Standards
|
||||
|
||||
### Format Priority
|
||||
|
||||
**If template defines output format** → Follow template format EXACTLY (all sections mandatory)
|
||||
|
||||
**If template has no format** → Use default format below based on task type
|
||||
|
||||
### Default Output Formats
|
||||
|
||||
#### Single Task Implementation
|
||||
|
||||
```markdown
|
||||
# Implementation: [TASK Title]
|
||||
|
||||
## Changes
|
||||
- Created: `path/to/file1.ext` (X lines)
|
||||
- Modified: `path/to/file2.ext` (+Y/-Z lines)
|
||||
- Deleted: `path/to/file3.ext`
|
||||
|
||||
## Summary
|
||||
[2-3 sentence overview of what was implemented]
|
||||
|
||||
## Key Decisions
|
||||
1. [Decision] - Rationale and reference to similar pattern
|
||||
2. [Decision] - path/to/reference:line
|
||||
|
||||
## Implementation Details
|
||||
[Evidence-based description with code references]
|
||||
|
||||
## Testing
|
||||
- Tests written: X new tests
|
||||
- Tests passing: Y/Z tests
|
||||
- Coverage: N%
|
||||
|
||||
## Validation
|
||||
✅ Tests: X passing
|
||||
✅ Coverage: Y%
|
||||
✅ Build: Success
|
||||
|
||||
## Next Steps
|
||||
[Recommendations or future improvements]
|
||||
```
|
||||
|
||||
#### Multi-Task Execution (with Resume)
|
||||
|
||||
**First Subtask**:
|
||||
```markdown
|
||||
# Subtask 1/N: [TASK Title]
|
||||
|
||||
## Changes
|
||||
[List of file changes]
|
||||
|
||||
## Implementation
|
||||
[Details with code references]
|
||||
|
||||
## Testing
|
||||
✅ Tests: X passing
|
||||
✅ Integration: Compatible with existing code
|
||||
|
||||
## Context for Next Subtask
|
||||
- Key decisions: [established patterns]
|
||||
- Files created: [paths and purposes]
|
||||
- Integration points: [where next subtask should connect]
|
||||
```
|
||||
|
||||
**Subsequent Subtasks**:
|
||||
```markdown
|
||||
# Subtask N/M: [TASK Title]
|
||||
|
||||
## Changes
|
||||
[List of file changes]
|
||||
|
||||
## Integration Notes
|
||||
✅ Compatible with subtask N-1
|
||||
✅ Maintains established patterns
|
||||
✅ Tests pass with previous work
|
||||
|
||||
## Implementation
|
||||
[Details with code references]
|
||||
|
||||
## Testing
|
||||
✅ Tests: X passing
|
||||
✅ Total coverage: Y%
|
||||
|
||||
## Context for Next Subtask
|
||||
[If not final subtask, provide context for continuation]
|
||||
```
|
||||
|
||||
#### Partial Completion
|
||||
|
||||
```markdown
|
||||
# Task Status: Partially Completed
|
||||
|
||||
## Completed
|
||||
- [What worked successfully]
|
||||
- Files: `path/to/completed.ext`
|
||||
|
||||
## Blocked
|
||||
- **Issue**: [What failed]
|
||||
- **Root Cause**: [Analysis of failure]
|
||||
- **Attempted**: [Solutions tried - attempt X of 3]
|
||||
|
||||
## Required
|
||||
[What's needed to proceed]
|
||||
|
||||
## Recommendation
|
||||
[Suggested next steps or alternative approaches]
|
||||
```
|
||||
|
||||
### Code References
|
||||
|
||||
**Format**: `path/to/file:line_number`
|
||||
|
||||
**Example**: `src/auth/jwt.ts:45` - Implemented token validation following pattern from `src/auth/session.ts:78`
|
||||
|
||||
### Related Files Section
|
||||
|
||||
**Always include at output beginning** - List ALL files analyzed, created, or modified:
|
||||
|
||||
```markdown
|
||||
## Related Files
|
||||
- `path/to/file1.ext` - [Role in implementation]
|
||||
- `path/to/file2.ext` - [Reference pattern used]
|
||||
- `path/to/file3.ext` - [Modified for X reason]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Three-Attempt Rule
|
||||
|
||||
**On 3rd failed attempt**:
|
||||
1. Stop execution
|
||||
2. Report: What attempted, what failed, root cause
|
||||
3. Request guidance or suggest alternatives
|
||||
|
||||
### Recovery Strategies
|
||||
|
||||
| Error Type | Response |
|
||||
|------------|----------|
|
||||
| **Syntax/Type** | Review errors → Fix → Re-run tests → Validate build |
|
||||
| **Runtime** | Analyze stack trace → Add error handling → Test error cases |
|
||||
| **Test Failure** | Debug in isolation → Review setup → Fix implementation/test |
|
||||
| **Build Failure** | Check messages → Fix incrementally → Validate each fix |
|
||||
|
||||
## Quality Standards
|
||||
|
||||
### Code Quality
|
||||
- Follow project's existing patterns
|
||||
- Match import style and naming conventions
|
||||
- Single responsibility per function/class
|
||||
- DRY (Don't Repeat Yourself)
|
||||
- YAGNI (You Aren't Gonna Need It)
|
||||
|
||||
### Testing
|
||||
- Test all public functions
|
||||
- Test edge cases and error conditions
|
||||
- Mock external dependencies
|
||||
- Target 80%+ coverage
|
||||
|
||||
### Error Handling
|
||||
- Proper try-catch blocks
|
||||
- Clear error messages
|
||||
- Graceful degradation
|
||||
- Don't expose sensitive info
|
||||
|
||||
## Core Principles
|
||||
|
||||
**Incremental Progress**:
|
||||
- Small, testable changes
|
||||
- Commit working code frequently
|
||||
- Build on previous work (subtasks)
|
||||
|
||||
**Evidence-Based**:
|
||||
- Study 3+ similar patterns before implementing
|
||||
- Match project style exactly
|
||||
- Verify with existing code
|
||||
|
||||
**Pragmatic**:
|
||||
- Boring solutions over clever code
|
||||
- Simple over complex
|
||||
- Adapt to project reality
|
||||
|
||||
**Context Continuity** (Multi-Task):
|
||||
- Leverage resume for consistency
|
||||
- Maintain established patterns
|
||||
- Test integration between subtasks
|
||||
|
||||
## Execution Checklist
|
||||
|
||||
**Before**:
|
||||
- [ ] Understand PURPOSE and TASK clearly
|
||||
- [ ] Review CONTEXT files, find 3+ patterns
|
||||
- [ ] Check RULES templates and constraints
|
||||
|
||||
**During**:
|
||||
- [ ] Follow existing patterns exactly
|
||||
- [ ] Write tests alongside code
|
||||
- [ ] Run tests after every change
|
||||
- [ ] Commit working code incrementally
|
||||
|
||||
**After**:
|
||||
- [ ] All tests pass
|
||||
- [ ] Coverage meets target
|
||||
- [ ] Build succeeds
|
||||
- [ ] All EXPECTED deliverables met
|
||||
196
API_SETTINGS_IMPLEMENTATION.md
Normal file
196
API_SETTINGS_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# API Settings 页面实现完成
|
||||
|
||||
## 创建的文件
|
||||
|
||||
### 1. JavaScript 文件
|
||||
**位置**: `ccw/src/templates/dashboard-js/views/api-settings.js` (28KB)
|
||||
|
||||
**主要功能**:
|
||||
- ✅ Provider Management (提供商管理)
|
||||
- 添加/编辑/删除提供商
|
||||
- 支持 OpenAI, Anthropic, Google, Ollama, Azure, Mistral, DeepSeek, Custom
|
||||
- API Key 管理(支持环境变量)
|
||||
- 连接测试功能
|
||||
|
||||
- ✅ Endpoint Management (端点管理)
|
||||
- 创建自定义端点
|
||||
- 关联提供商和模型
|
||||
- 缓存策略配置
|
||||
- 显示 CLI 使用示例
|
||||
|
||||
- ✅ Cache Management (缓存管理)
|
||||
- 全局缓存开关
|
||||
- 缓存统计显示
|
||||
- 清除缓存功能
|
||||
|
||||
### 2. CSS 样式文件
|
||||
**位置**: `ccw/src/templates/dashboard-css/31-api-settings.css` (6.8KB)
|
||||
|
||||
**样式包括**:
|
||||
- 卡片式布局
|
||||
- 表单样式
|
||||
- 进度条
|
||||
- 响应式设计
|
||||
- 空状态显示
|
||||
|
||||
### 3. 国际化支持
|
||||
**位置**: `ccw/src/templates/dashboard-js/i18n.js`
|
||||
|
||||
**添加的翻译**:
|
||||
- 英文:54 个翻译键
|
||||
- 中文:54 个翻译键
|
||||
- 包含所有 UI 文本、提示信息、错误消息
|
||||
|
||||
### 4. 配置更新
|
||||
|
||||
#### dashboard-generator.ts
|
||||
- ✅ 添加 `31-api-settings.css` 到 CSS 模块列表
|
||||
- ✅ 添加 `views/api-settings.js` 到 JS 模块列表
|
||||
|
||||
#### navigation.js
|
||||
- ✅ 添加 `api-settings` 路由处理
|
||||
- ✅ 添加标题更新逻辑
|
||||
|
||||
#### dashboard.html
|
||||
- ✅ 添加导航菜单项 (Settings 图标)
|
||||
|
||||
## API 端点使用
|
||||
|
||||
该页面使用以下后端 API(已存在):
|
||||
|
||||
### Provider APIs
|
||||
- `GET /api/litellm-api/providers` - 获取所有提供商
|
||||
- `POST /api/litellm-api/providers` - 创建提供商
|
||||
- `PUT /api/litellm-api/providers/:id` - 更新提供商
|
||||
- `DELETE /api/litellm-api/providers/:id` - 删除提供商
|
||||
- `POST /api/litellm-api/providers/:id/test` - 测试连接
|
||||
|
||||
### Endpoint APIs
|
||||
- `GET /api/litellm-api/endpoints` - 获取所有端点
|
||||
- `POST /api/litellm-api/endpoints` - 创建端点
|
||||
- `PUT /api/litellm-api/endpoints/:id` - 更新端点
|
||||
- `DELETE /api/litellm-api/endpoints/:id` - 删除端点
|
||||
|
||||
### Model Discovery
|
||||
- `GET /api/litellm-api/models/:providerType` - 获取提供商支持的模型列表
|
||||
|
||||
### Cache APIs
|
||||
- `GET /api/litellm-api/cache/stats` - 获取缓存统计
|
||||
- `POST /api/litellm-api/cache/clear` - 清除缓存
|
||||
|
||||
### Config APIs
|
||||
- `GET /api/litellm-api/config` - 获取完整配置
|
||||
- `PUT /api/litellm-api/config/cache` - 更新全局缓存设置
|
||||
|
||||
## 页面特性
|
||||
|
||||
### Provider 管理
|
||||
```
|
||||
+-- Provider Card ------------------------+
|
||||
| OpenAI Production [Edit] [Del] |
|
||||
| Type: openai |
|
||||
| Key: sk-...abc |
|
||||
| URL: https://api.openai.com/v1 |
|
||||
| Status: ✓ Enabled |
|
||||
+-----------------------------------------+
|
||||
```
|
||||
|
||||
### Endpoint 管理
|
||||
```
|
||||
+-- Endpoint Card ------------------------+
|
||||
| GPT-4o Code Review [Edit] [Del]|
|
||||
| ID: my-gpt4o |
|
||||
| Provider: OpenAI Production |
|
||||
| Model: gpt-4-turbo |
|
||||
| Cache: Enabled (60 min) |
|
||||
| Usage: ccw cli -p "..." --model my-gpt4o|
|
||||
+-----------------------------------------+
|
||||
```
|
||||
|
||||
### 表单功能
|
||||
- **Provider Form**:
|
||||
- 类型选择(8 种提供商)
|
||||
- API Key 输入(支持显示/隐藏)
|
||||
- 环境变量支持
|
||||
- Base URL 自定义
|
||||
- 启用/禁用开关
|
||||
|
||||
- **Endpoint Form**:
|
||||
- 端点 ID(CLI 使用)
|
||||
- 显示名称
|
||||
- 提供商选择(动态加载)
|
||||
- 模型选择(根据提供商动态加载)
|
||||
- 缓存策略配置
|
||||
- TTL(分钟)
|
||||
- 最大大小(KB)
|
||||
- 自动缓存文件模式
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 添加提供商
|
||||
1. 点击 "Add Provider"
|
||||
2. 选择提供商类型(如 OpenAI)
|
||||
3. 输入显示名称
|
||||
4. 输入 API Key(或使用环境变量)
|
||||
5. 可选:输入自定义 API Base URL
|
||||
6. 保存
|
||||
|
||||
### 2. 创建自定义端点
|
||||
1. 点击 "Add Endpoint"
|
||||
2. 输入端点 ID(用于 CLI)
|
||||
3. 输入显示名称
|
||||
4. 选择提供商
|
||||
5. 选择模型(自动加载该提供商支持的模型)
|
||||
6. 可选:配置缓存策略
|
||||
7. 保存
|
||||
|
||||
### 3. 使用端点
|
||||
```bash
|
||||
ccw cli -p "Analyze this code..." --model my-gpt4o
|
||||
```
|
||||
|
||||
## 代码质量
|
||||
|
||||
- ✅ 遵循现有代码风格
|
||||
- ✅ 使用 i18n 函数支持国际化
|
||||
- ✅ 响应式设计(移动端友好)
|
||||
- ✅ 完整的表单验证
|
||||
- ✅ 用户友好的错误提示
|
||||
- ✅ 使用 Lucide 图标
|
||||
- ✅ 模态框复用现有样式
|
||||
- ✅ 与后端 API 完全集成
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. **基础功能测试**:
|
||||
- 添加/编辑/删除提供商
|
||||
- 添加/编辑/删除端点
|
||||
- 清除缓存
|
||||
|
||||
2. **表单验证测试**:
|
||||
- 必填字段验证
|
||||
- API Key 显示/隐藏
|
||||
- 环境变量切换
|
||||
|
||||
3. **数据加载测试**:
|
||||
- 模型列表动态加载
|
||||
- 缓存统计显示
|
||||
- 空状态显示
|
||||
|
||||
4. **国际化测试**:
|
||||
- 切换语言(英文/中文)
|
||||
- 验证所有文本正确显示
|
||||
|
||||
## 下一步
|
||||
|
||||
页面已完成并集成到项目中。启动 CCW Dashboard 后:
|
||||
1. 导航栏会显示 "API Settings" 菜单项(Settings 图标)
|
||||
2. 点击进入即可使用所有功能
|
||||
3. 所有操作会实时同步到配置文件
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 页面使用现有的 LiteLLM API 路由(`litellm-api-routes.ts`)
|
||||
- 配置保存在项目的 LiteLLM 配置文件中
|
||||
- 支持环境变量引用格式:`${VARIABLE_NAME}`
|
||||
- API Key 在显示时会自动脱敏(显示前 4 位和后 4 位)
|
||||
12
README.md
12
README.md
@@ -5,7 +5,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/catlog22/Claude-Code-Workflow/releases)
|
||||
[](https://github.com/catlog22/Claude-Code-Workflow/releases)
|
||||
[](https://www.npmjs.com/package/claude-code-workflow)
|
||||
[](LICENSE)
|
||||
[]()
|
||||
@@ -52,6 +52,16 @@ CCW is built on a set of core principles that distinguish it from traditional AI
|
||||
|
||||
## ⚙️ Installation
|
||||
|
||||
### **📋 Requirements**
|
||||
|
||||
| Platform | Node.js | Additional |
|
||||
|----------|---------|------------|
|
||||
| Windows | 20.x or 22.x LTS (recommended) | Node 23+ requires [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) |
|
||||
| macOS | 18.x+ | Xcode Command Line Tools |
|
||||
| Linux | 18.x+ | build-essential |
|
||||
|
||||
> **Note**: The `better-sqlite3` dependency requires native compilation. Using Node.js LTS versions avoids build issues.
|
||||
|
||||
### **📦 npm Install (Recommended)**
|
||||
|
||||
Install globally via npm:
|
||||
|
||||
32
README_CN.md
32
README_CN.md
@@ -1,5 +1,8 @@
|
||||
# 🚀 Claude Code Workflow (CCW)
|
||||
|
||||
[](https://smithery.ai/skills?ns=catlog22&utm_source=github&utm_medium=badge)
|
||||
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/catlog22/Claude-Code-Workflow/releases)
|
||||
@@ -13,7 +16,7 @@
|
||||
|
||||
---
|
||||
|
||||
**Claude Code Workflow (CCW)** 将 AI 开发从简单的提示词链接转变为一个强大的、上下文优先的编排系统。它通过结构化规划、确定性执行和智能多模型编排,解决了执行不确定性和误差累积的问题。
|
||||
**Claude Code Workflow (CCW)** 是一个 JSON 驱动的多智能体开发框架,具有智能 CLI 编排(Gemini/Qwen/Codex)、上下文优先架构和自动化工作流执行。它将 AI 开发从简单的提示词链接转变为一个强大的编排系统。
|
||||
|
||||
> **🎉 版本 6.2.0: 原生 CodexLens 与 Dashboard 革新**
|
||||
>
|
||||
@@ -38,8 +41,8 @@
|
||||
|
||||
CCW 构建在一系列核心原则之上,这些原则使其与传统的 AI 开发方法区别开来:
|
||||
|
||||
- **上下文优先架构**: 通过预定义的上下文收集,消除了执行过程中的不确定性,确保智能体在实现*之前*就拥有正确的信息。
|
||||
- **JSON 优先的状态管理**: 任务状态完全存储在 `.task/IMPL-*.json` 文件中,作为唯一的事实来源,实现了无需状态漂移的程序化编排。
|
||||
- **上下文优先架构**: 通过预定义的上下文收集消除执行过程中的不确定性,确保智能体在实现*之前*就拥有正确的信息。
|
||||
- **JSON 优先的状态管理**: 任务状态完全存储在 `.task/IMPL-*.json` 文件中作为唯一的事实来源,实现无状态漂移的程序化编排。
|
||||
- **自主多阶段编排**: 命令链式调用专门的子命令和智能体,以零用户干预的方式自动化复杂的工作流。
|
||||
- **多模型策略**: 充分利用不同 AI 模型(如 Gemini 用于分析,Codex 用于实现)的独特优势,以获得更优越的结果。
|
||||
- **分层内存系统**: 一个 4 层文档系统,在适当的抽象级别上提供上下文,防止信息过载。
|
||||
@@ -49,18 +52,23 @@ CCW 构建在一系列核心原则之上,这些原则使其与传统的 AI 开
|
||||
|
||||
## ⚙️ 安装
|
||||
|
||||
有关详细的安装说明,请参阅 [**INSTALL_CN.md**](INSTALL_CN.md) 指南。
|
||||
### **📦 npm 安装(推荐)**
|
||||
|
||||
### **🚀 一键快速安装**
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
Invoke-Expression (Invoke-WebRequest -Uri "https://raw.githubusercontent.com/catlog22/Claude-Code-Workflow/main/install-remote.ps1" -UseBasicParsing).Content
|
||||
通过 npm 全局安装:
|
||||
```bash
|
||||
npm install -g claude-code-workflow
|
||||
```
|
||||
|
||||
**Linux/macOS (Bash/Zsh):**
|
||||
然后将工作流文件安装到您的系统:
|
||||
```bash
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/catlog22/Claude-Code-Workflow/main/install-remote.sh)
|
||||
# 交互式安装
|
||||
ccw install
|
||||
|
||||
# 全局安装(到 ~/.claude)
|
||||
ccw install -m Global
|
||||
|
||||
# 项目特定安装
|
||||
ccw install -m Path -p /path/to/project
|
||||
```
|
||||
|
||||
### **✅ 验证安装**
|
||||
@@ -283,4 +291,4 @@ CCW 提供全面的文档,帮助您快速上手并掌握高级功能:
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
此项目根据 **MIT 许可证** 授权。详见 [LICENSE](LICENSE) 文件。
|
||||
此项目根据 **MIT 许可证** 授权。详见 [LICENSE](LICENSE) 文件。
|
||||
|
||||
180
ccw-litellm/README.md
Normal file
180
ccw-litellm/README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# ccw-litellm
|
||||
|
||||
Unified LiteLLM interface layer shared by ccw and codex-lens projects.
|
||||
|
||||
## Features
|
||||
|
||||
- **Unified LLM Interface**: Abstract interface for LLM operations (chat, completion)
|
||||
- **Unified Embedding Interface**: Abstract interface for text embeddings
|
||||
- **Multi-Provider Support**: OpenAI, Anthropic, Azure, and more via LiteLLM
|
||||
- **Configuration Management**: YAML-based configuration with environment variable substitution
|
||||
- **Type Safety**: Full type annotations with Pydantic models
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a configuration file at `~/.ccw/config/litellm-config.yaml`:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
default_provider: openai
|
||||
|
||||
providers:
|
||||
openai:
|
||||
api_key: ${OPENAI_API_KEY}
|
||||
api_base: https://api.openai.com/v1
|
||||
|
||||
llm_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: gpt-4
|
||||
|
||||
embedding_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: text-embedding-3-small
|
||||
dimensions: 1536
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
#### LLM Client
|
||||
|
||||
```python
|
||||
from ccw_litellm import LiteLLMClient, ChatMessage
|
||||
|
||||
# Initialize client with default model
|
||||
client = LiteLLMClient(model="default")
|
||||
|
||||
# Chat completion
|
||||
messages = [
|
||||
ChatMessage(role="user", content="Hello, how are you?")
|
||||
]
|
||||
response = client.chat(messages)
|
||||
print(response.content)
|
||||
|
||||
# Text completion
|
||||
response = client.complete("Once upon a time")
|
||||
print(response.content)
|
||||
```
|
||||
|
||||
#### Embedder
|
||||
|
||||
```python
|
||||
from ccw_litellm import LiteLLMEmbedder
|
||||
|
||||
# Initialize embedder with default model
|
||||
embedder = LiteLLMEmbedder(model="default")
|
||||
|
||||
# Embed single text
|
||||
vector = embedder.embed("Hello world")
|
||||
print(vector.shape) # (1, 1536)
|
||||
|
||||
# Embed multiple texts
|
||||
vectors = embedder.embed(["Text 1", "Text 2", "Text 3"])
|
||||
print(vectors.shape) # (3, 1536)
|
||||
```
|
||||
|
||||
#### Custom Configuration
|
||||
|
||||
```python
|
||||
from ccw_litellm import LiteLLMClient, load_config
|
||||
|
||||
# Load custom configuration
|
||||
config = load_config("/path/to/custom-config.yaml")
|
||||
|
||||
# Use custom configuration
|
||||
client = LiteLLMClient(model="fast", config=config)
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Provider Configuration
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
<provider_name>:
|
||||
api_key: <api_key_or_${ENV_VAR}>
|
||||
api_base: <base_url>
|
||||
```
|
||||
|
||||
Supported providers: `openai`, `anthropic`, `azure`, `vertex_ai`, `bedrock`, etc.
|
||||
|
||||
### LLM Model Configuration
|
||||
|
||||
```yaml
|
||||
llm_models:
|
||||
<model_name>:
|
||||
provider: <provider_name>
|
||||
model: <model_identifier>
|
||||
```
|
||||
|
||||
### Embedding Model Configuration
|
||||
|
||||
```yaml
|
||||
embedding_models:
|
||||
<model_name>:
|
||||
provider: <provider_name>
|
||||
model: <model_identifier>
|
||||
dimensions: <embedding_dimensions>
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The configuration supports environment variable substitution using the `${VAR}` or `${VAR:-default}` syntax:
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
openai:
|
||||
api_key: ${OPENAI_API_KEY} # Required
|
||||
api_base: ${OPENAI_API_BASE:-https://api.openai.com/v1} # With default
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Interfaces
|
||||
|
||||
- `AbstractLLMClient`: Abstract base class for LLM clients
|
||||
- `AbstractEmbedder`: Abstract base class for embedders
|
||||
- `ChatMessage`: Message data class (role, content)
|
||||
- `LLMResponse`: Response data class (content, raw)
|
||||
|
||||
### Implementations
|
||||
|
||||
- `LiteLLMClient`: LiteLLM implementation of AbstractLLMClient
|
||||
- `LiteLLMEmbedder`: LiteLLM implementation of AbstractEmbedder
|
||||
|
||||
### Configuration
|
||||
|
||||
- `LiteLLMConfig`: Root configuration model
|
||||
- `ProviderConfig`: Provider configuration model
|
||||
- `LLMModelConfig`: LLM model configuration model
|
||||
- `EmbeddingModelConfig`: Embedding model configuration model
|
||||
- `load_config(path)`: Load configuration from YAML file
|
||||
- `get_config(path, reload)`: Get global configuration singleton
|
||||
- `reset_config()`: Reset global configuration (for testing)
|
||||
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
pytest tests/ -v
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
|
||||
```bash
|
||||
mypy src/ccw_litellm
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
53
ccw-litellm/litellm-config.yaml.example
Normal file
53
ccw-litellm/litellm-config.yaml.example
Normal file
@@ -0,0 +1,53 @@
|
||||
# LiteLLM Unified Configuration
|
||||
# Copy to ~/.ccw/config/litellm-config.yaml
|
||||
|
||||
version: 1
|
||||
|
||||
# Default provider for LLM calls
|
||||
default_provider: openai
|
||||
|
||||
# Provider configurations
|
||||
providers:
|
||||
openai:
|
||||
api_key: ${OPENAI_API_KEY}
|
||||
api_base: https://api.openai.com/v1
|
||||
|
||||
anthropic:
|
||||
api_key: ${ANTHROPIC_API_KEY}
|
||||
|
||||
ollama:
|
||||
api_base: http://localhost:11434
|
||||
|
||||
azure:
|
||||
api_key: ${AZURE_API_KEY}
|
||||
api_base: ${AZURE_API_BASE}
|
||||
|
||||
# LLM model configurations
|
||||
llm_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
fast:
|
||||
provider: openai
|
||||
model: gpt-4o-mini
|
||||
claude:
|
||||
provider: anthropic
|
||||
model: claude-sonnet-4-20250514
|
||||
local:
|
||||
provider: ollama
|
||||
model: llama3.2
|
||||
|
||||
# Embedding model configurations
|
||||
embedding_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: text-embedding-3-small
|
||||
dimensions: 1536
|
||||
large:
|
||||
provider: openai
|
||||
model: text-embedding-3-large
|
||||
dimensions: 3072
|
||||
ada:
|
||||
provider: openai
|
||||
model: text-embedding-ada-002
|
||||
dimensions: 1536
|
||||
35
ccw-litellm/pyproject.toml
Normal file
35
ccw-litellm/pyproject.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "ccw-litellm"
|
||||
version = "0.1.0"
|
||||
description = "Unified LiteLLM interface layer shared by ccw and codex-lens"
|
||||
requires-python = ">=3.10"
|
||||
authors = [{ name = "ccw-litellm contributors" }]
|
||||
dependencies = [
|
||||
"litellm>=1.0.0",
|
||||
"pyyaml",
|
||||
"numpy",
|
||||
"pydantic>=2.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
ccw-litellm = "ccw_litellm.cli:main"
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = { "" = "src" }
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
include = ["ccw_litellm*"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
addopts = "-q"
|
||||
12
ccw-litellm/src/ccw_litellm.egg-info/PKG-INFO
Normal file
12
ccw-litellm/src/ccw_litellm.egg-info/PKG-INFO
Normal file
@@ -0,0 +1,12 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: ccw-litellm
|
||||
Version: 0.1.0
|
||||
Summary: Unified LiteLLM interface layer shared by ccw and codex-lens
|
||||
Author: ccw-litellm contributors
|
||||
Requires-Python: >=3.10
|
||||
Requires-Dist: litellm>=1.0.0
|
||||
Requires-Dist: pyyaml
|
||||
Requires-Dist: numpy
|
||||
Requires-Dist: pydantic>=2.0
|
||||
Provides-Extra: dev
|
||||
Requires-Dist: pytest>=7.0; extra == "dev"
|
||||
20
ccw-litellm/src/ccw_litellm.egg-info/SOURCES.txt
Normal file
20
ccw-litellm/src/ccw_litellm.egg-info/SOURCES.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
README.md
|
||||
pyproject.toml
|
||||
src/ccw_litellm/__init__.py
|
||||
src/ccw_litellm/cli.py
|
||||
src/ccw_litellm.egg-info/PKG-INFO
|
||||
src/ccw_litellm.egg-info/SOURCES.txt
|
||||
src/ccw_litellm.egg-info/dependency_links.txt
|
||||
src/ccw_litellm.egg-info/entry_points.txt
|
||||
src/ccw_litellm.egg-info/requires.txt
|
||||
src/ccw_litellm.egg-info/top_level.txt
|
||||
src/ccw_litellm/clients/__init__.py
|
||||
src/ccw_litellm/clients/litellm_embedder.py
|
||||
src/ccw_litellm/clients/litellm_llm.py
|
||||
src/ccw_litellm/config/__init__.py
|
||||
src/ccw_litellm/config/loader.py
|
||||
src/ccw_litellm/config/models.py
|
||||
src/ccw_litellm/interfaces/__init__.py
|
||||
src/ccw_litellm/interfaces/embedder.py
|
||||
src/ccw_litellm/interfaces/llm.py
|
||||
tests/test_interfaces.py
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
2
ccw-litellm/src/ccw_litellm.egg-info/entry_points.txt
Normal file
2
ccw-litellm/src/ccw_litellm.egg-info/entry_points.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
ccw-litellm = ccw_litellm.cli:main
|
||||
7
ccw-litellm/src/ccw_litellm.egg-info/requires.txt
Normal file
7
ccw-litellm/src/ccw_litellm.egg-info/requires.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
litellm>=1.0.0
|
||||
pyyaml
|
||||
numpy
|
||||
pydantic>=2.0
|
||||
|
||||
[dev]
|
||||
pytest>=7.0
|
||||
1
ccw-litellm/src/ccw_litellm.egg-info/top_level.txt
Normal file
1
ccw-litellm/src/ccw_litellm.egg-info/top_level.txt
Normal file
@@ -0,0 +1 @@
|
||||
ccw_litellm
|
||||
47
ccw-litellm/src/ccw_litellm/__init__.py
Normal file
47
ccw-litellm/src/ccw_litellm/__init__.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""ccw-litellm package.
|
||||
|
||||
This package provides a small, stable interface layer around LiteLLM to share
|
||||
between the ccw and codex-lens projects.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .clients import LiteLLMClient, LiteLLMEmbedder
|
||||
from .config import (
|
||||
EmbeddingModelConfig,
|
||||
LiteLLMConfig,
|
||||
LLMModelConfig,
|
||||
ProviderConfig,
|
||||
get_config,
|
||||
load_config,
|
||||
reset_config,
|
||||
)
|
||||
from .interfaces import (
|
||||
AbstractEmbedder,
|
||||
AbstractLLMClient,
|
||||
ChatMessage,
|
||||
LLMResponse,
|
||||
)
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
# Abstract interfaces
|
||||
"AbstractEmbedder",
|
||||
"AbstractLLMClient",
|
||||
"ChatMessage",
|
||||
"LLMResponse",
|
||||
# Client implementations
|
||||
"LiteLLMClient",
|
||||
"LiteLLMEmbedder",
|
||||
# Configuration
|
||||
"LiteLLMConfig",
|
||||
"ProviderConfig",
|
||||
"LLMModelConfig",
|
||||
"EmbeddingModelConfig",
|
||||
"load_config",
|
||||
"get_config",
|
||||
"reset_config",
|
||||
]
|
||||
|
||||
108
ccw-litellm/src/ccw_litellm/cli.py
Normal file
108
ccw-litellm/src/ccw_litellm/cli.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""CLI entry point for ccw-litellm."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Main CLI entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="ccw-litellm",
|
||||
description="Unified LiteLLM interface for ccw and codex-lens",
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
||||
|
||||
# config command
|
||||
config_parser = subparsers.add_parser("config", help="Show configuration")
|
||||
config_parser.add_argument(
|
||||
"--path",
|
||||
type=Path,
|
||||
help="Configuration file path",
|
||||
)
|
||||
|
||||
# embed command
|
||||
embed_parser = subparsers.add_parser("embed", help="Generate embeddings")
|
||||
embed_parser.add_argument("texts", nargs="+", help="Texts to embed")
|
||||
embed_parser.add_argument(
|
||||
"--model",
|
||||
default="default",
|
||||
help="Embedding model name (default: default)",
|
||||
)
|
||||
embed_parser.add_argument(
|
||||
"--output",
|
||||
choices=["json", "shape"],
|
||||
default="shape",
|
||||
help="Output format (default: shape)",
|
||||
)
|
||||
|
||||
# chat command
|
||||
chat_parser = subparsers.add_parser("chat", help="Chat with LLM")
|
||||
chat_parser.add_argument("message", help="Message to send")
|
||||
chat_parser.add_argument(
|
||||
"--model",
|
||||
default="default",
|
||||
help="LLM model name (default: default)",
|
||||
)
|
||||
|
||||
# version command
|
||||
subparsers.add_parser("version", help="Show version")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "version":
|
||||
from . import __version__
|
||||
|
||||
print(f"ccw-litellm {__version__}")
|
||||
return 0
|
||||
|
||||
if args.command == "config":
|
||||
from .config import get_config
|
||||
|
||||
try:
|
||||
config = get_config(config_path=args.path if hasattr(args, "path") else None)
|
||||
print(config.model_dump_json(indent=2))
|
||||
except Exception as e:
|
||||
print(f"Error loading config: {e}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if args.command == "embed":
|
||||
from .clients import LiteLLMEmbedder
|
||||
|
||||
try:
|
||||
embedder = LiteLLMEmbedder(model=args.model)
|
||||
vectors = embedder.embed(args.texts)
|
||||
|
||||
if args.output == "json":
|
||||
print(json.dumps(vectors.tolist()))
|
||||
else:
|
||||
print(f"Shape: {vectors.shape}")
|
||||
print(f"Dimensions: {embedder.dimensions}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if args.command == "chat":
|
||||
from .clients import LiteLLMClient
|
||||
from .interfaces import ChatMessage
|
||||
|
||||
try:
|
||||
client = LiteLLMClient(model=args.model)
|
||||
response = client.chat([ChatMessage(role="user", content=args.message)])
|
||||
print(response.content)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
12
ccw-litellm/src/ccw_litellm/clients/__init__.py
Normal file
12
ccw-litellm/src/ccw_litellm/clients/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""Client implementations for ccw-litellm."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .litellm_embedder import LiteLLMEmbedder
|
||||
from .litellm_llm import LiteLLMClient
|
||||
|
||||
__all__ = [
|
||||
"LiteLLMClient",
|
||||
"LiteLLMEmbedder",
|
||||
]
|
||||
|
||||
251
ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py
Normal file
251
ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""LiteLLM embedder implementation for text embeddings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Sequence
|
||||
|
||||
import litellm
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..config import LiteLLMConfig, get_config
|
||||
from ..interfaces.embedder import AbstractEmbedder
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LiteLLMEmbedder(AbstractEmbedder):
|
||||
"""LiteLLM embedder implementation.
|
||||
|
||||
Supports multiple embedding providers (OpenAI, etc.) through LiteLLM's unified interface.
|
||||
|
||||
Example:
|
||||
embedder = LiteLLMEmbedder(model="default")
|
||||
vectors = embedder.embed(["Hello world", "Another text"])
|
||||
print(vectors.shape) # (2, 1536)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: str = "default",
|
||||
config: LiteLLMConfig | None = None,
|
||||
**litellm_kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize LiteLLM embedder.
|
||||
|
||||
Args:
|
||||
model: Model name from configuration (default: "default")
|
||||
config: Configuration instance (default: use global config)
|
||||
**litellm_kwargs: Additional arguments to pass to litellm.embedding()
|
||||
"""
|
||||
self._config = config or get_config()
|
||||
self._model_name = model
|
||||
self._litellm_kwargs = litellm_kwargs
|
||||
|
||||
# Get embedding model configuration
|
||||
try:
|
||||
self._model_config = self._config.get_embedding_model(model)
|
||||
except ValueError as e:
|
||||
logger.error(f"Failed to get embedding model configuration: {e}")
|
||||
raise
|
||||
|
||||
# Get provider configuration
|
||||
try:
|
||||
self._provider_config = self._config.get_provider(self._model_config.provider)
|
||||
except ValueError as e:
|
||||
logger.error(f"Failed to get provider configuration: {e}")
|
||||
raise
|
||||
|
||||
# Set up LiteLLM environment
|
||||
self._setup_litellm()
|
||||
|
||||
def _setup_litellm(self) -> None:
|
||||
"""Configure LiteLLM with provider settings."""
|
||||
provider = self._model_config.provider
|
||||
|
||||
# Set API key
|
||||
if self._provider_config.api_key:
|
||||
litellm.api_key = self._provider_config.api_key
|
||||
# Also set environment-specific keys
|
||||
if provider == "openai":
|
||||
litellm.openai_key = self._provider_config.api_key
|
||||
elif provider == "anthropic":
|
||||
litellm.anthropic_key = self._provider_config.api_key
|
||||
|
||||
# Set API base
|
||||
if self._provider_config.api_base:
|
||||
litellm.api_base = self._provider_config.api_base
|
||||
|
||||
def _format_model_name(self) -> str:
|
||||
"""Format model name for LiteLLM.
|
||||
|
||||
Returns:
|
||||
Formatted model name (e.g., "openai/text-embedding-3-small")
|
||||
"""
|
||||
provider = self._model_config.provider
|
||||
model = self._model_config.model
|
||||
|
||||
# For some providers, LiteLLM expects explicit prefix
|
||||
if provider in ["azure", "vertex_ai", "bedrock"]:
|
||||
return f"{provider}/{model}"
|
||||
|
||||
# For providers with custom api_base (OpenAI-compatible endpoints),
|
||||
# use openai/ prefix to tell LiteLLM to use OpenAI API format
|
||||
if self._provider_config.api_base and provider not in ["openai", "anthropic"]:
|
||||
return f"openai/{model}"
|
||||
|
||||
return model
|
||||
|
||||
@property
|
||||
def dimensions(self) -> int:
|
||||
"""Embedding vector size."""
|
||||
return self._model_config.dimensions
|
||||
|
||||
def _estimate_tokens(self, text: str) -> int:
|
||||
"""Estimate token count for a text using fast heuristic.
|
||||
|
||||
Args:
|
||||
text: Text to estimate tokens for
|
||||
|
||||
Returns:
|
||||
Estimated token count (len/4 is a reasonable approximation)
|
||||
"""
|
||||
return len(text) // 4
|
||||
|
||||
def _create_batches(
|
||||
self,
|
||||
texts: list[str],
|
||||
max_tokens: int = 30000
|
||||
) -> list[list[str]]:
|
||||
"""Split texts into batches that fit within token limits.
|
||||
|
||||
Args:
|
||||
texts: List of texts to batch
|
||||
max_tokens: Maximum tokens per batch (default: 30000, safe margin for 40960 limit)
|
||||
|
||||
Returns:
|
||||
List of text batches
|
||||
"""
|
||||
batches = []
|
||||
current_batch = []
|
||||
current_tokens = 0
|
||||
|
||||
for text in texts:
|
||||
text_tokens = self._estimate_tokens(text)
|
||||
|
||||
# If single text exceeds limit, truncate it
|
||||
if text_tokens > max_tokens:
|
||||
logger.warning(f"Text with {text_tokens} estimated tokens exceeds limit, truncating")
|
||||
# Truncate to fit (rough estimate: 4 chars per token)
|
||||
max_chars = max_tokens * 4
|
||||
text = text[:max_chars]
|
||||
text_tokens = self._estimate_tokens(text)
|
||||
|
||||
# Start new batch if current would exceed limit
|
||||
if current_tokens + text_tokens > max_tokens and current_batch:
|
||||
batches.append(current_batch)
|
||||
current_batch = []
|
||||
current_tokens = 0
|
||||
|
||||
current_batch.append(text)
|
||||
current_tokens += text_tokens
|
||||
|
||||
# Add final batch
|
||||
if current_batch:
|
||||
batches.append(current_batch)
|
||||
|
||||
return batches
|
||||
|
||||
def embed(
|
||||
self,
|
||||
texts: str | Sequence[str],
|
||||
*,
|
||||
batch_size: int | None = None,
|
||||
max_tokens_per_batch: int = 30000,
|
||||
**kwargs: Any,
|
||||
) -> NDArray[np.floating]:
|
||||
"""Embed one or more texts.
|
||||
|
||||
Args:
|
||||
texts: Single text or sequence of texts
|
||||
batch_size: Batch size for processing (deprecated, use max_tokens_per_batch)
|
||||
max_tokens_per_batch: Maximum estimated tokens per API call (default: 30000)
|
||||
**kwargs: Additional arguments for litellm.embedding()
|
||||
|
||||
Returns:
|
||||
A numpy array of shape (n_texts, dimensions).
|
||||
|
||||
Raises:
|
||||
Exception: If LiteLLM embedding fails
|
||||
"""
|
||||
# Normalize input to list
|
||||
if isinstance(texts, str):
|
||||
text_list = [texts]
|
||||
else:
|
||||
text_list = list(texts)
|
||||
|
||||
if not text_list:
|
||||
# Return empty array with correct shape
|
||||
return np.empty((0, self.dimensions), dtype=np.float32)
|
||||
|
||||
# Merge kwargs
|
||||
embedding_kwargs = {**self._litellm_kwargs, **kwargs}
|
||||
|
||||
# For OpenAI-compatible endpoints, ensure encoding_format is set
|
||||
if self._provider_config.api_base and "encoding_format" not in embedding_kwargs:
|
||||
embedding_kwargs["encoding_format"] = "float"
|
||||
|
||||
# Split into token-aware batches
|
||||
batches = self._create_batches(text_list, max_tokens_per_batch)
|
||||
|
||||
if len(batches) > 1:
|
||||
logger.info(f"Split {len(text_list)} texts into {len(batches)} batches for embedding")
|
||||
|
||||
all_embeddings = []
|
||||
|
||||
for batch_idx, batch in enumerate(batches):
|
||||
try:
|
||||
# Build call kwargs with explicit api_base
|
||||
call_kwargs = {**embedding_kwargs}
|
||||
if self._provider_config.api_base:
|
||||
call_kwargs["api_base"] = self._provider_config.api_base
|
||||
if self._provider_config.api_key:
|
||||
call_kwargs["api_key"] = self._provider_config.api_key
|
||||
|
||||
# Call LiteLLM embedding for this batch
|
||||
response = litellm.embedding(
|
||||
model=self._format_model_name(),
|
||||
input=batch,
|
||||
**call_kwargs,
|
||||
)
|
||||
|
||||
# Extract embeddings
|
||||
batch_embeddings = [item["embedding"] for item in response.data]
|
||||
all_embeddings.extend(batch_embeddings)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LiteLLM embedding failed for batch {batch_idx + 1}/{len(batches)}: {e}")
|
||||
raise
|
||||
|
||||
# Convert to numpy array
|
||||
result = np.array(all_embeddings, dtype=np.float32)
|
||||
|
||||
# Validate dimensions
|
||||
if result.shape[1] != self.dimensions:
|
||||
logger.warning(
|
||||
f"Expected {self.dimensions} dimensions, got {result.shape[1]}. "
|
||||
f"Configuration may be incorrect."
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
@property
|
||||
def model_name(self) -> str:
|
||||
"""Get configured model name."""
|
||||
return self._model_name
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
"""Get configured provider name."""
|
||||
return self._model_config.provider
|
||||
165
ccw-litellm/src/ccw_litellm/clients/litellm_llm.py
Normal file
165
ccw-litellm/src/ccw_litellm/clients/litellm_llm.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""LiteLLM client implementation for LLM operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Sequence
|
||||
|
||||
import litellm
|
||||
|
||||
from ..config import LiteLLMConfig, get_config
|
||||
from ..interfaces.llm import AbstractLLMClient, ChatMessage, LLMResponse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LiteLLMClient(AbstractLLMClient):
|
||||
"""LiteLLM client implementation.
|
||||
|
||||
Supports multiple providers (OpenAI, Anthropic, etc.) through LiteLLM's unified interface.
|
||||
|
||||
Example:
|
||||
client = LiteLLMClient(model="default")
|
||||
response = client.chat([
|
||||
ChatMessage(role="user", content="Hello!")
|
||||
])
|
||||
print(response.content)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: str = "default",
|
||||
config: LiteLLMConfig | None = None,
|
||||
**litellm_kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize LiteLLM client.
|
||||
|
||||
Args:
|
||||
model: Model name from configuration (default: "default")
|
||||
config: Configuration instance (default: use global config)
|
||||
**litellm_kwargs: Additional arguments to pass to litellm.completion()
|
||||
"""
|
||||
self._config = config or get_config()
|
||||
self._model_name = model
|
||||
self._litellm_kwargs = litellm_kwargs
|
||||
|
||||
# Get model configuration
|
||||
try:
|
||||
self._model_config = self._config.get_llm_model(model)
|
||||
except ValueError as e:
|
||||
logger.error(f"Failed to get model configuration: {e}")
|
||||
raise
|
||||
|
||||
# Get provider configuration
|
||||
try:
|
||||
self._provider_config = self._config.get_provider(self._model_config.provider)
|
||||
except ValueError as e:
|
||||
logger.error(f"Failed to get provider configuration: {e}")
|
||||
raise
|
||||
|
||||
# Set up LiteLLM environment
|
||||
self._setup_litellm()
|
||||
|
||||
def _setup_litellm(self) -> None:
|
||||
"""Configure LiteLLM with provider settings."""
|
||||
provider = self._model_config.provider
|
||||
|
||||
# Set API key
|
||||
if self._provider_config.api_key:
|
||||
env_var = f"{provider.upper()}_API_KEY"
|
||||
litellm.api_key = self._provider_config.api_key
|
||||
# Also set environment-specific keys
|
||||
if provider == "openai":
|
||||
litellm.openai_key = self._provider_config.api_key
|
||||
elif provider == "anthropic":
|
||||
litellm.anthropic_key = self._provider_config.api_key
|
||||
|
||||
# Set API base
|
||||
if self._provider_config.api_base:
|
||||
litellm.api_base = self._provider_config.api_base
|
||||
|
||||
def _format_model_name(self) -> str:
|
||||
"""Format model name for LiteLLM.
|
||||
|
||||
Returns:
|
||||
Formatted model name (e.g., "gpt-4", "claude-3-opus-20240229")
|
||||
"""
|
||||
# LiteLLM expects model names in format: "provider/model" or just "model"
|
||||
# If provider is explicit, use provider/model format
|
||||
provider = self._model_config.provider
|
||||
model = self._model_config.model
|
||||
|
||||
# For some providers, LiteLLM expects explicit prefix
|
||||
if provider in ["anthropic", "azure", "vertex_ai", "bedrock"]:
|
||||
return f"{provider}/{model}"
|
||||
|
||||
return model
|
||||
|
||||
def chat(
|
||||
self,
|
||||
messages: Sequence[ChatMessage],
|
||||
**kwargs: Any,
|
||||
) -> LLMResponse:
|
||||
"""Chat completion for a sequence of messages.
|
||||
|
||||
Args:
|
||||
messages: Sequence of chat messages
|
||||
**kwargs: Additional arguments for litellm.completion()
|
||||
|
||||
Returns:
|
||||
LLM response with content and raw response
|
||||
|
||||
Raises:
|
||||
Exception: If LiteLLM completion fails
|
||||
"""
|
||||
# Convert messages to LiteLLM format
|
||||
litellm_messages = [
|
||||
{"role": msg.role, "content": msg.content} for msg in messages
|
||||
]
|
||||
|
||||
# Merge kwargs
|
||||
completion_kwargs = {**self._litellm_kwargs, **kwargs}
|
||||
|
||||
try:
|
||||
# Call LiteLLM
|
||||
response = litellm.completion(
|
||||
model=self._format_model_name(),
|
||||
messages=litellm_messages,
|
||||
**completion_kwargs,
|
||||
)
|
||||
|
||||
# Extract content
|
||||
content = response.choices[0].message.content or ""
|
||||
|
||||
return LLMResponse(content=content, raw=response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LiteLLM completion failed: {e}")
|
||||
raise
|
||||
|
||||
def complete(self, prompt: str, **kwargs: Any) -> LLMResponse:
|
||||
"""Text completion for a prompt.
|
||||
|
||||
Args:
|
||||
prompt: Input prompt
|
||||
**kwargs: Additional arguments for litellm.completion()
|
||||
|
||||
Returns:
|
||||
LLM response with content and raw response
|
||||
|
||||
Raises:
|
||||
Exception: If LiteLLM completion fails
|
||||
"""
|
||||
# Convert to chat format (most modern models use chat interface)
|
||||
messages = [ChatMessage(role="user", content=prompt)]
|
||||
return self.chat(messages, **kwargs)
|
||||
|
||||
@property
|
||||
def model_name(self) -> str:
|
||||
"""Get configured model name."""
|
||||
return self._model_name
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
"""Get configured provider name."""
|
||||
return self._model_config.provider
|
||||
22
ccw-litellm/src/ccw_litellm/config/__init__.py
Normal file
22
ccw-litellm/src/ccw_litellm/config/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Configuration management for LiteLLM integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .loader import get_config, load_config, reset_config
|
||||
from .models import (
|
||||
EmbeddingModelConfig,
|
||||
LiteLLMConfig,
|
||||
LLMModelConfig,
|
||||
ProviderConfig,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"LiteLLMConfig",
|
||||
"ProviderConfig",
|
||||
"LLMModelConfig",
|
||||
"EmbeddingModelConfig",
|
||||
"load_config",
|
||||
"get_config",
|
||||
"reset_config",
|
||||
]
|
||||
|
||||
316
ccw-litellm/src/ccw_litellm/config/loader.py
Normal file
316
ccw-litellm/src/ccw_litellm/config/loader.py
Normal file
@@ -0,0 +1,316 @@
|
||||
"""Configuration loader with environment variable substitution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
from .models import LiteLLMConfig
|
||||
|
||||
# Default configuration paths
|
||||
# JSON format (UI config) takes priority over YAML format
|
||||
DEFAULT_JSON_CONFIG_PATH = Path.home() / ".ccw" / "config" / "litellm-api-config.json"
|
||||
DEFAULT_YAML_CONFIG_PATH = Path.home() / ".ccw" / "config" / "litellm-config.yaml"
|
||||
# Keep backward compatibility
|
||||
DEFAULT_CONFIG_PATH = DEFAULT_YAML_CONFIG_PATH
|
||||
|
||||
# Global configuration singleton
|
||||
_config_instance: LiteLLMConfig | None = None
|
||||
|
||||
|
||||
def _substitute_env_vars(value: Any) -> Any:
|
||||
"""Recursively substitute environment variables in configuration values.
|
||||
|
||||
Supports ${ENV_VAR} and ${ENV_VAR:-default} syntax.
|
||||
|
||||
Args:
|
||||
value: Configuration value (str, dict, list, or primitive)
|
||||
|
||||
Returns:
|
||||
Value with environment variables substituted
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
# Pattern: ${VAR} or ${VAR:-default}
|
||||
pattern = r"\$\{([^:}]+)(?::-(.*?))?\}"
|
||||
|
||||
def replace_var(match: re.Match) -> str:
|
||||
var_name = match.group(1)
|
||||
default_value = match.group(2) if match.group(2) is not None else ""
|
||||
return os.environ.get(var_name, default_value)
|
||||
|
||||
return re.sub(pattern, replace_var, value)
|
||||
|
||||
if isinstance(value, dict):
|
||||
return {k: _substitute_env_vars(v) for k, v in value.items()}
|
||||
|
||||
if isinstance(value, list):
|
||||
return [_substitute_env_vars(item) for item in value]
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _get_default_config() -> dict[str, Any]:
|
||||
"""Get default configuration when no config file exists.
|
||||
|
||||
Returns:
|
||||
Default configuration dictionary
|
||||
"""
|
||||
return {
|
||||
"version": 1,
|
||||
"default_provider": "openai",
|
||||
"providers": {
|
||||
"openai": {
|
||||
"api_key": "${OPENAI_API_KEY}",
|
||||
"api_base": "https://api.openai.com/v1",
|
||||
},
|
||||
},
|
||||
"llm_models": {
|
||||
"default": {
|
||||
"provider": "openai",
|
||||
"model": "gpt-4",
|
||||
},
|
||||
"fast": {
|
||||
"provider": "openai",
|
||||
"model": "gpt-3.5-turbo",
|
||||
},
|
||||
},
|
||||
"embedding_models": {
|
||||
"default": {
|
||||
"provider": "openai",
|
||||
"model": "text-embedding-3-small",
|
||||
"dimensions": 1536,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _convert_json_to_internal_format(json_config: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Convert UI JSON config format to internal format.
|
||||
|
||||
The UI stores config in a different structure:
|
||||
- providers: array of {id, name, type, apiKey, apiBase, llmModels[], embeddingModels[]}
|
||||
|
||||
Internal format uses:
|
||||
- providers: dict of {provider_id: {api_key, api_base}}
|
||||
- llm_models: dict of {model_id: {provider, model}}
|
||||
- embedding_models: dict of {model_id: {provider, model, dimensions}}
|
||||
|
||||
Args:
|
||||
json_config: Configuration in UI JSON format
|
||||
|
||||
Returns:
|
||||
Configuration in internal format
|
||||
"""
|
||||
providers: dict[str, Any] = {}
|
||||
llm_models: dict[str, Any] = {}
|
||||
embedding_models: dict[str, Any] = {}
|
||||
default_provider: str | None = None
|
||||
|
||||
for provider in json_config.get("providers", []):
|
||||
if not provider.get("enabled", True):
|
||||
continue
|
||||
|
||||
provider_id = provider.get("id", "")
|
||||
if not provider_id:
|
||||
continue
|
||||
|
||||
# Set first enabled provider as default
|
||||
if default_provider is None:
|
||||
default_provider = provider_id
|
||||
|
||||
# Convert provider with advanced settings
|
||||
provider_config: dict[str, Any] = {
|
||||
"api_key": provider.get("apiKey", ""),
|
||||
"api_base": provider.get("apiBase"),
|
||||
}
|
||||
|
||||
# Map advanced settings
|
||||
adv = provider.get("advancedSettings", {})
|
||||
if adv.get("timeout"):
|
||||
provider_config["timeout"] = adv["timeout"]
|
||||
if adv.get("maxRetries"):
|
||||
provider_config["max_retries"] = adv["maxRetries"]
|
||||
if adv.get("organization"):
|
||||
provider_config["organization"] = adv["organization"]
|
||||
if adv.get("apiVersion"):
|
||||
provider_config["api_version"] = adv["apiVersion"]
|
||||
if adv.get("customHeaders"):
|
||||
provider_config["custom_headers"] = adv["customHeaders"]
|
||||
|
||||
providers[provider_id] = provider_config
|
||||
|
||||
# Convert LLM models
|
||||
for model in provider.get("llmModels", []):
|
||||
if not model.get("enabled", True):
|
||||
continue
|
||||
model_id = model.get("id", "")
|
||||
if not model_id:
|
||||
continue
|
||||
|
||||
llm_model_config: dict[str, Any] = {
|
||||
"provider": provider_id,
|
||||
"model": model.get("name", ""),
|
||||
}
|
||||
# Add model-specific endpoint settings
|
||||
endpoint = model.get("endpointSettings", {})
|
||||
if endpoint.get("baseUrl"):
|
||||
llm_model_config["api_base"] = endpoint["baseUrl"]
|
||||
if endpoint.get("timeout"):
|
||||
llm_model_config["timeout"] = endpoint["timeout"]
|
||||
if endpoint.get("maxRetries"):
|
||||
llm_model_config["max_retries"] = endpoint["maxRetries"]
|
||||
|
||||
# Add capabilities
|
||||
caps = model.get("capabilities", {})
|
||||
if caps.get("contextWindow"):
|
||||
llm_model_config["context_window"] = caps["contextWindow"]
|
||||
if caps.get("maxOutputTokens"):
|
||||
llm_model_config["max_output_tokens"] = caps["maxOutputTokens"]
|
||||
|
||||
llm_models[model_id] = llm_model_config
|
||||
|
||||
# Convert embedding models
|
||||
for model in provider.get("embeddingModels", []):
|
||||
if not model.get("enabled", True):
|
||||
continue
|
||||
model_id = model.get("id", "")
|
||||
if not model_id:
|
||||
continue
|
||||
|
||||
embedding_model_config: dict[str, Any] = {
|
||||
"provider": provider_id,
|
||||
"model": model.get("name", ""),
|
||||
"dimensions": model.get("capabilities", {}).get("embeddingDimension", 1536),
|
||||
}
|
||||
# Add model-specific endpoint settings
|
||||
endpoint = model.get("endpointSettings", {})
|
||||
if endpoint.get("baseUrl"):
|
||||
embedding_model_config["api_base"] = endpoint["baseUrl"]
|
||||
if endpoint.get("timeout"):
|
||||
embedding_model_config["timeout"] = endpoint["timeout"]
|
||||
|
||||
embedding_models[model_id] = embedding_model_config
|
||||
|
||||
# Ensure we have defaults if no models found
|
||||
if not llm_models:
|
||||
llm_models["default"] = {
|
||||
"provider": default_provider or "openai",
|
||||
"model": "gpt-4",
|
||||
}
|
||||
|
||||
if not embedding_models:
|
||||
embedding_models["default"] = {
|
||||
"provider": default_provider or "openai",
|
||||
"model": "text-embedding-3-small",
|
||||
"dimensions": 1536,
|
||||
}
|
||||
|
||||
return {
|
||||
"version": json_config.get("version", 1),
|
||||
"default_provider": default_provider or "openai",
|
||||
"providers": providers,
|
||||
"llm_models": llm_models,
|
||||
"embedding_models": embedding_models,
|
||||
}
|
||||
|
||||
|
||||
def load_config(config_path: Path | str | None = None) -> LiteLLMConfig:
|
||||
"""Load LiteLLM configuration from JSON or YAML file.
|
||||
|
||||
Priority order:
|
||||
1. Explicit config_path if provided
|
||||
2. JSON config (UI format): ~/.ccw/config/litellm-api-config.json
|
||||
3. YAML config: ~/.ccw/config/litellm-config.yaml
|
||||
4. Default configuration
|
||||
|
||||
Args:
|
||||
config_path: Path to configuration file (optional)
|
||||
|
||||
Returns:
|
||||
Parsed and validated configuration
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If config file not found and no default available
|
||||
ValueError: If configuration is invalid
|
||||
"""
|
||||
raw_config: dict[str, Any] | None = None
|
||||
is_json_format = False
|
||||
|
||||
if config_path is not None:
|
||||
config_path = Path(config_path)
|
||||
if config_path.exists():
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
if config_path.suffix == ".json":
|
||||
raw_config = json.load(f)
|
||||
is_json_format = True
|
||||
else:
|
||||
raw_config = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to load configuration from {config_path}: {e}") from e
|
||||
|
||||
# Check JSON config first (UI format)
|
||||
if raw_config is None and DEFAULT_JSON_CONFIG_PATH.exists():
|
||||
try:
|
||||
with open(DEFAULT_JSON_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
raw_config = json.load(f)
|
||||
is_json_format = True
|
||||
except Exception:
|
||||
pass # Fall through to YAML
|
||||
|
||||
# Check YAML config
|
||||
if raw_config is None and DEFAULT_YAML_CONFIG_PATH.exists():
|
||||
try:
|
||||
with open(DEFAULT_YAML_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
raw_config = yaml.safe_load(f)
|
||||
except Exception:
|
||||
pass # Fall through to default
|
||||
|
||||
# Use default configuration
|
||||
if raw_config is None:
|
||||
raw_config = _get_default_config()
|
||||
|
||||
# Convert JSON format to internal format if needed
|
||||
if is_json_format:
|
||||
raw_config = _convert_json_to_internal_format(raw_config)
|
||||
|
||||
# Substitute environment variables
|
||||
config_data = _substitute_env_vars(raw_config)
|
||||
|
||||
# Validate and parse with Pydantic
|
||||
try:
|
||||
return LiteLLMConfig.model_validate(config_data)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid configuration: {e}") from e
|
||||
|
||||
|
||||
def get_config(config_path: Path | str | None = None, reload: bool = False) -> LiteLLMConfig:
|
||||
"""Get global configuration singleton.
|
||||
|
||||
Args:
|
||||
config_path: Path to configuration file (default: ~/.ccw/config/litellm-config.yaml)
|
||||
reload: Force reload configuration from disk
|
||||
|
||||
Returns:
|
||||
Global configuration instance
|
||||
"""
|
||||
global _config_instance
|
||||
|
||||
if _config_instance is None or reload:
|
||||
_config_instance = load_config(config_path)
|
||||
|
||||
return _config_instance
|
||||
|
||||
|
||||
def reset_config() -> None:
|
||||
"""Reset global configuration singleton.
|
||||
|
||||
Useful for testing.
|
||||
"""
|
||||
global _config_instance
|
||||
_config_instance = None
|
||||
130
ccw-litellm/src/ccw_litellm/config/models.py
Normal file
130
ccw-litellm/src/ccw_litellm/config/models.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""Pydantic configuration models for LiteLLM integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ProviderConfig(BaseModel):
|
||||
"""Provider API configuration.
|
||||
|
||||
Supports environment variable substitution in the format ${ENV_VAR}.
|
||||
"""
|
||||
|
||||
api_key: str | None = None
|
||||
api_base: str | None = None
|
||||
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
|
||||
class LLMModelConfig(BaseModel):
|
||||
"""LLM model configuration."""
|
||||
|
||||
provider: str
|
||||
model: str
|
||||
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
|
||||
class EmbeddingModelConfig(BaseModel):
|
||||
"""Embedding model configuration."""
|
||||
|
||||
provider: str # "openai", "fastembed", "ollama", etc.
|
||||
model: str
|
||||
dimensions: int
|
||||
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
|
||||
class LiteLLMConfig(BaseModel):
|
||||
"""Root configuration for LiteLLM integration.
|
||||
|
||||
Example YAML:
|
||||
version: 1
|
||||
default_provider: openai
|
||||
providers:
|
||||
openai:
|
||||
api_key: ${OPENAI_API_KEY}
|
||||
api_base: https://api.openai.com/v1
|
||||
anthropic:
|
||||
api_key: ${ANTHROPIC_API_KEY}
|
||||
llm_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: gpt-4
|
||||
fast:
|
||||
provider: openai
|
||||
model: gpt-3.5-turbo
|
||||
embedding_models:
|
||||
default:
|
||||
provider: openai
|
||||
model: text-embedding-3-small
|
||||
dimensions: 1536
|
||||
"""
|
||||
|
||||
version: int = 1
|
||||
default_provider: str = "openai"
|
||||
providers: dict[str, ProviderConfig] = Field(default_factory=dict)
|
||||
llm_models: dict[str, LLMModelConfig] = Field(default_factory=dict)
|
||||
embedding_models: dict[str, EmbeddingModelConfig] = Field(default_factory=dict)
|
||||
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
def get_llm_model(self, model: str = "default") -> LLMModelConfig:
|
||||
"""Get LLM model configuration by name.
|
||||
|
||||
Args:
|
||||
model: Model name or "default"
|
||||
|
||||
Returns:
|
||||
LLM model configuration
|
||||
|
||||
Raises:
|
||||
ValueError: If model not found
|
||||
"""
|
||||
if model not in self.llm_models:
|
||||
raise ValueError(
|
||||
f"LLM model '{model}' not found in configuration. "
|
||||
f"Available models: {list(self.llm_models.keys())}"
|
||||
)
|
||||
return self.llm_models[model]
|
||||
|
||||
def get_embedding_model(self, model: str = "default") -> EmbeddingModelConfig:
|
||||
"""Get embedding model configuration by name.
|
||||
|
||||
Args:
|
||||
model: Model name or "default"
|
||||
|
||||
Returns:
|
||||
Embedding model configuration
|
||||
|
||||
Raises:
|
||||
ValueError: If model not found
|
||||
"""
|
||||
if model not in self.embedding_models:
|
||||
raise ValueError(
|
||||
f"Embedding model '{model}' not found in configuration. "
|
||||
f"Available models: {list(self.embedding_models.keys())}"
|
||||
)
|
||||
return self.embedding_models[model]
|
||||
|
||||
def get_provider(self, provider: str) -> ProviderConfig:
|
||||
"""Get provider configuration by name.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
Provider configuration
|
||||
|
||||
Raises:
|
||||
ValueError: If provider not found
|
||||
"""
|
||||
if provider not in self.providers:
|
||||
raise ValueError(
|
||||
f"Provider '{provider}' not found in configuration. "
|
||||
f"Available providers: {list(self.providers.keys())}"
|
||||
)
|
||||
return self.providers[provider]
|
||||
14
ccw-litellm/src/ccw_litellm/interfaces/__init__.py
Normal file
14
ccw-litellm/src/ccw_litellm/interfaces/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Abstract interfaces for ccw-litellm."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .embedder import AbstractEmbedder
|
||||
from .llm import AbstractLLMClient, ChatMessage, LLMResponse
|
||||
|
||||
__all__ = [
|
||||
"AbstractEmbedder",
|
||||
"AbstractLLMClient",
|
||||
"ChatMessage",
|
||||
"LLMResponse",
|
||||
]
|
||||
|
||||
52
ccw-litellm/src/ccw_litellm/interfaces/embedder.py
Normal file
52
ccw-litellm/src/ccw_litellm/interfaces/embedder.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Sequence
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
|
||||
class AbstractEmbedder(ABC):
|
||||
"""Embedding interface compatible with fastembed-style embedders.
|
||||
|
||||
Implementers only need to provide the synchronous `embed` method; an
|
||||
asynchronous `aembed` wrapper is provided for convenience.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def dimensions(self) -> int:
|
||||
"""Embedding vector size."""
|
||||
|
||||
@abstractmethod
|
||||
def embed(
|
||||
self,
|
||||
texts: str | Sequence[str],
|
||||
*,
|
||||
batch_size: int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> NDArray[np.floating]:
|
||||
"""Embed one or more texts.
|
||||
|
||||
Returns:
|
||||
A numpy array of shape (n_texts, dimensions).
|
||||
"""
|
||||
|
||||
async def aembed(
|
||||
self,
|
||||
texts: str | Sequence[str],
|
||||
*,
|
||||
batch_size: int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> NDArray[np.floating]:
|
||||
"""Async wrapper around `embed` using a worker thread by default."""
|
||||
|
||||
return await asyncio.to_thread(
|
||||
self.embed,
|
||||
texts,
|
||||
batch_size=batch_size,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
45
ccw-litellm/src/ccw_litellm/interfaces/llm.py
Normal file
45
ccw-litellm/src/ccw_litellm/interfaces/llm.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Literal, Sequence
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ChatMessage:
|
||||
role: Literal["system", "user", "assistant", "tool"]
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class LLMResponse:
|
||||
content: str
|
||||
raw: Any | None = None
|
||||
|
||||
|
||||
class AbstractLLMClient(ABC):
|
||||
"""LiteLLM-like client interface.
|
||||
|
||||
Implementers only need to provide synchronous methods; async wrappers are
|
||||
provided via `asyncio.to_thread`.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> LLMResponse:
|
||||
"""Chat completion for a sequence of messages."""
|
||||
|
||||
@abstractmethod
|
||||
def complete(self, prompt: str, **kwargs: Any) -> LLMResponse:
|
||||
"""Text completion for a prompt."""
|
||||
|
||||
async def achat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> LLMResponse:
|
||||
"""Async wrapper around `chat` using a worker thread by default."""
|
||||
|
||||
return await asyncio.to_thread(self.chat, messages, **kwargs)
|
||||
|
||||
async def acomplete(self, prompt: str, **kwargs: Any) -> LLMResponse:
|
||||
"""Async wrapper around `complete` using a worker thread by default."""
|
||||
|
||||
return await asyncio.to_thread(self.complete, prompt, **kwargs)
|
||||
|
||||
11
ccw-litellm/tests/conftest.py
Normal file
11
ccw-litellm/tests/conftest.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def pytest_configure() -> None:
|
||||
project_root = Path(__file__).resolve().parents[1]
|
||||
src_dir = project_root / "src"
|
||||
sys.path.insert(0, str(src_dir))
|
||||
|
||||
64
ccw-litellm/tests/test_interfaces.py
Normal file
64
ccw-litellm/tests/test_interfaces.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any, Sequence
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ccw_litellm.interfaces import AbstractEmbedder, AbstractLLMClient, ChatMessage, LLMResponse
|
||||
|
||||
|
||||
class _DummyEmbedder(AbstractEmbedder):
|
||||
@property
|
||||
def dimensions(self) -> int:
|
||||
return 3
|
||||
|
||||
def embed(
|
||||
self,
|
||||
texts: str | Sequence[str],
|
||||
*,
|
||||
batch_size: int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> np.ndarray:
|
||||
if isinstance(texts, str):
|
||||
texts = [texts]
|
||||
_ = batch_size
|
||||
_ = kwargs
|
||||
return np.zeros((len(texts), self.dimensions), dtype=np.float32)
|
||||
|
||||
|
||||
class _DummyLLM(AbstractLLMClient):
|
||||
def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> LLMResponse:
|
||||
_ = kwargs
|
||||
return LLMResponse(content="".join(m.content for m in messages))
|
||||
|
||||
def complete(self, prompt: str, **kwargs: Any) -> LLMResponse:
|
||||
_ = kwargs
|
||||
return LLMResponse(content=prompt)
|
||||
|
||||
|
||||
def test_embed_sync_shape_and_dtype() -> None:
|
||||
emb = _DummyEmbedder()
|
||||
out = emb.embed(["a", "b"])
|
||||
assert out.shape == (2, 3)
|
||||
assert out.dtype == np.float32
|
||||
|
||||
|
||||
def test_embed_async_wrapper() -> None:
|
||||
emb = _DummyEmbedder()
|
||||
out = asyncio.run(emb.aembed("x"))
|
||||
assert out.shape == (1, 3)
|
||||
|
||||
|
||||
def test_llm_sync() -> None:
|
||||
llm = _DummyLLM()
|
||||
out = llm.chat([ChatMessage(role="user", content="hi")])
|
||||
assert out == LLMResponse(content="hi")
|
||||
|
||||
|
||||
def test_llm_async_wrappers() -> None:
|
||||
llm = _DummyLLM()
|
||||
out1 = asyncio.run(llm.achat([ChatMessage(role="user", content="a")]))
|
||||
out2 = asyncio.run(llm.acomplete("b"))
|
||||
assert out1.content == "a"
|
||||
assert out2.content == "b"
|
||||
1
ccw/.gitignore
vendored
1
ccw/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
|
||||
# TypeScript build output
|
||||
dist/
|
||||
.ace-tool/
|
||||
|
||||
15
ccw/.npmignore
Normal file
15
ccw/.npmignore
Normal file
@@ -0,0 +1,15 @@
|
||||
# npm ignore file - overrides .gitignore for npm publish
|
||||
# dist/ is NOT excluded here so it gets published
|
||||
|
||||
# Development files
|
||||
node_modules/
|
||||
*.log
|
||||
*.tmp
|
||||
|
||||
# Test files
|
||||
tests/
|
||||
*.test.js
|
||||
*.spec.js
|
||||
|
||||
# TypeScript source maps (optional, can keep for debugging)
|
||||
# *.map
|
||||
308
ccw/LITELLM_INTEGRATION.md
Normal file
308
ccw/LITELLM_INTEGRATION.md
Normal file
@@ -0,0 +1,308 @@
|
||||
# LiteLLM Integration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
CCW now supports custom LiteLLM endpoints with integrated context caching. You can configure multiple providers (OpenAI, Anthropic, Ollama, etc.) and create custom endpoints with file-based caching strategies.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CLI Executor │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────────────────────┐ │
|
||||
│ │ --model │────────>│ Route Decision: │ │
|
||||
│ │ flag │ │ - gemini/qwen/codex → CLI │ │
|
||||
│ └─────────────┘ │ - custom ID → LiteLLM │ │
|
||||
│ └──────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LiteLLM Executor │
|
||||
│ │
|
||||
│ 1. Load endpoint config (litellm-api-config.json) │
|
||||
│ 2. Extract @patterns from prompt │
|
||||
│ 3. Pack files via context-cache │
|
||||
│ 4. Call LiteLLM client with cached content + prompt │
|
||||
│ 5. Return result │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### File Location
|
||||
|
||||
Configuration is stored per-project:
|
||||
```
|
||||
<project>/.ccw/storage/config/litellm-api-config.json
|
||||
```
|
||||
|
||||
### Configuration Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"providers": [
|
||||
{
|
||||
"id": "openai-1234567890",
|
||||
"name": "My OpenAI",
|
||||
"type": "openai",
|
||||
"apiKey": "${OPENAI_API_KEY}",
|
||||
"enabled": true,
|
||||
"createdAt": "2025-01-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-01-01T00:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"endpoints": [
|
||||
{
|
||||
"id": "my-gpt4o",
|
||||
"name": "GPT-4o with Context Cache",
|
||||
"providerId": "openai-1234567890",
|
||||
"model": "gpt-4o",
|
||||
"description": "GPT-4o with automatic file caching",
|
||||
"cacheStrategy": {
|
||||
"enabled": true,
|
||||
"ttlMinutes": 60,
|
||||
"maxSizeKB": 512,
|
||||
"filePatterns": ["*.md", "*.ts", "*.js"]
|
||||
},
|
||||
"enabled": true,
|
||||
"createdAt": "2025-01-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-01-01T00:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"defaultEndpoint": "my-gpt4o",
|
||||
"globalCacheSettings": {
|
||||
"enabled": true,
|
||||
"cacheDir": "~/.ccw/cache/context",
|
||||
"maxTotalSizeMB": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Via CLI
|
||||
|
||||
```bash
|
||||
# Use custom endpoint with --model flag
|
||||
ccw cli -p "Analyze authentication flow" --tool litellm --model my-gpt4o
|
||||
|
||||
# With context patterns (automatically cached)
|
||||
ccw cli -p "@src/auth/**/*.ts Review security" --tool litellm --model my-gpt4o
|
||||
|
||||
# Disable caching for specific call
|
||||
ccw cli -p "Quick question" --tool litellm --model my-gpt4o --no-cache
|
||||
```
|
||||
|
||||
### Via Dashboard API
|
||||
|
||||
#### Create Provider
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/litellm-api/providers \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "My OpenAI",
|
||||
"type": "openai",
|
||||
"apiKey": "${OPENAI_API_KEY}",
|
||||
"enabled": true
|
||||
}'
|
||||
```
|
||||
|
||||
#### Create Endpoint
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/litellm-api/endpoints \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "my-gpt4o",
|
||||
"name": "GPT-4o with Cache",
|
||||
"providerId": "openai-1234567890",
|
||||
"model": "gpt-4o",
|
||||
"cacheStrategy": {
|
||||
"enabled": true,
|
||||
"ttlMinutes": 60,
|
||||
"maxSizeKB": 512,
|
||||
"filePatterns": ["*.md", "*.ts"]
|
||||
},
|
||||
"enabled": true
|
||||
}'
|
||||
```
|
||||
|
||||
#### Test Provider Connection
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/litellm-api/providers/openai-1234567890/test
|
||||
```
|
||||
|
||||
## Context Caching
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Pattern Detection**: LiteLLM executor scans prompt for `@patterns`
|
||||
```
|
||||
@src/**/*.ts
|
||||
@CLAUDE.md
|
||||
@../shared/**/*
|
||||
```
|
||||
|
||||
2. **File Packing**: Files matching patterns are packed via `context-cache` tool
|
||||
- Respects `max_file_size` limit (default: 1MB per file)
|
||||
- Applies TTL from endpoint config
|
||||
- Generates session ID for retrieval
|
||||
|
||||
3. **Cache Integration**: Cached content is prepended to prompt
|
||||
```
|
||||
<cached files>
|
||||
---
|
||||
<original prompt>
|
||||
```
|
||||
|
||||
4. **LLM Call**: Combined prompt sent to LiteLLM with provider credentials
|
||||
|
||||
### Cache Strategy Configuration
|
||||
|
||||
```typescript
|
||||
interface CacheStrategy {
|
||||
enabled: boolean; // Enable/disable caching for this endpoint
|
||||
ttlMinutes: number; // Cache lifetime (default: 60)
|
||||
maxSizeKB: number; // Max cache size (default: 512KB)
|
||||
filePatterns: string[]; // Glob patterns to cache
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Security Audit with Cache
|
||||
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: OWASP Top 10 security audit of authentication module
|
||||
TASK: • Check SQL injection • Verify session management • Test XSS vectors
|
||||
CONTEXT: @src/auth/**/*.ts @src/middleware/auth.ts
|
||||
EXPECTED: Security report with severity levels and remediation steps
|
||||
" --tool litellm --model my-security-scanner --mode analysis
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Executor detects `@src/auth/**/*.ts` and `@src/middleware/auth.ts`
|
||||
2. Packs matching files into context cache
|
||||
3. Cache entry valid for 60 minutes (per endpoint config)
|
||||
4. Subsequent calls reuse cached files (no re-packing)
|
||||
5. LiteLLM receives full context without manual file specification
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Provider API Keys
|
||||
|
||||
LiteLLM uses standard environment variable names:
|
||||
|
||||
| Provider | Env Var Name |
|
||||
|------------|-----------------------|
|
||||
| OpenAI | `OPENAI_API_KEY` |
|
||||
| Anthropic | `ANTHROPIC_API_KEY` |
|
||||
| Google | `GOOGLE_API_KEY` |
|
||||
| Azure | `AZURE_API_KEY` |
|
||||
| Mistral | `MISTRAL_API_KEY` |
|
||||
| DeepSeek | `DEEPSEEK_API_KEY` |
|
||||
|
||||
### Configuration Syntax
|
||||
|
||||
Use `${ENV_VAR}` syntax in config:
|
||||
```json
|
||||
{
|
||||
"apiKey": "${OPENAI_API_KEY}"
|
||||
}
|
||||
```
|
||||
|
||||
The executor resolves these at runtime via `resolveEnvVar()`.
|
||||
|
||||
## API Reference
|
||||
|
||||
### Config Manager (`litellm-api-config-manager.ts`)
|
||||
|
||||
#### Provider Management
|
||||
```typescript
|
||||
getAllProviders(baseDir: string): ProviderCredential[]
|
||||
getProvider(baseDir: string, providerId: string): ProviderCredential | null
|
||||
getProviderWithResolvedEnvVars(baseDir: string, providerId: string): ProviderCredential & { resolvedApiKey: string } | null
|
||||
addProvider(baseDir: string, providerData): ProviderCredential
|
||||
updateProvider(baseDir: string, providerId: string, updates): ProviderCredential
|
||||
deleteProvider(baseDir: string, providerId: string): boolean
|
||||
```
|
||||
|
||||
#### Endpoint Management
|
||||
```typescript
|
||||
getAllEndpoints(baseDir: string): CustomEndpoint[]
|
||||
getEndpoint(baseDir: string, endpointId: string): CustomEndpoint | null
|
||||
findEndpointById(baseDir: string, endpointId: string): CustomEndpoint | null
|
||||
addEndpoint(baseDir: string, endpointData): CustomEndpoint
|
||||
updateEndpoint(baseDir: string, endpointId: string, updates): CustomEndpoint
|
||||
deleteEndpoint(baseDir: string, endpointId: string): boolean
|
||||
```
|
||||
|
||||
### Executor (`litellm-executor.ts`)
|
||||
|
||||
```typescript
|
||||
interface LiteLLMExecutionOptions {
|
||||
prompt: string;
|
||||
endpointId: string;
|
||||
baseDir: string;
|
||||
cwd?: string;
|
||||
includeDirs?: string[];
|
||||
enableCache?: boolean;
|
||||
onOutput?: (data: { type: string; data: string }) => void;
|
||||
}
|
||||
|
||||
interface LiteLLMExecutionResult {
|
||||
success: boolean;
|
||||
output: string;
|
||||
model: string;
|
||||
provider: string;
|
||||
cacheUsed: boolean;
|
||||
cachedFiles?: string[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
executeLiteLLMEndpoint(options: LiteLLMExecutionOptions): Promise<LiteLLMExecutionResult>
|
||||
extractPatterns(prompt: string): string[]
|
||||
```
|
||||
|
||||
## Dashboard Integration
|
||||
|
||||
The dashboard provides UI for managing LiteLLM configuration:
|
||||
|
||||
- **Providers**: Add/edit/delete provider credentials
|
||||
- **Endpoints**: Configure custom endpoints with cache strategies
|
||||
- **Cache Stats**: View cache usage and clear entries
|
||||
- **Test Connections**: Verify provider API access
|
||||
|
||||
Routes are handled by `litellm-api-routes.ts`.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. **Python Dependency**: Requires `ccw-litellm` Python package installed
|
||||
2. **Model Support**: Limited to models supported by LiteLLM library
|
||||
3. **Cache Scope**: Context cache is in-memory (not persisted across restarts)
|
||||
4. **Pattern Syntax**: Only supports glob-style `@patterns`, not regex
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: "Endpoint not found"
|
||||
- Verify endpoint ID matches config file
|
||||
- Check `litellm-api-config.json` exists in `.ccw/storage/config/`
|
||||
|
||||
### Error: "API key not configured"
|
||||
- Ensure environment variable is set
|
||||
- Verify `${ENV_VAR}` syntax in config
|
||||
- Test with `echo $OPENAI_API_KEY`
|
||||
|
||||
### Error: "Failed to spawn Python process"
|
||||
- Install ccw-litellm: `pip install ccw-litellm`
|
||||
- Verify Python accessible: `python --version`
|
||||
|
||||
### Cache Not Applied
|
||||
- Check endpoint has `cacheStrategy.enabled: true`
|
||||
- Verify prompt contains `@patterns`
|
||||
- Check cache TTL hasn't expired
|
||||
|
||||
## Examples
|
||||
|
||||
See `examples/litellm-config.json` for complete configuration template.
|
||||
77
ccw/examples/litellm-usage.ts
Normal file
77
ccw/examples/litellm-usage.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* LiteLLM Usage Examples
|
||||
* Demonstrates how to use the LiteLLM TypeScript client
|
||||
*/
|
||||
|
||||
import { getLiteLLMClient, getLiteLLMStatus } from '../src/tools/litellm-client';
|
||||
|
||||
async function main() {
|
||||
console.log('=== LiteLLM TypeScript Bridge Examples ===\n');
|
||||
|
||||
// Example 1: Check availability
|
||||
console.log('1. Checking LiteLLM availability...');
|
||||
const status = await getLiteLLMStatus();
|
||||
console.log(' Status:', status);
|
||||
console.log('');
|
||||
|
||||
if (!status.available) {
|
||||
console.log('❌ LiteLLM is not available. Please install ccw-litellm:');
|
||||
console.log(' pip install ccw-litellm');
|
||||
return;
|
||||
}
|
||||
|
||||
const client = getLiteLLMClient();
|
||||
|
||||
// Example 2: Get configuration
|
||||
console.log('2. Getting configuration...');
|
||||
try {
|
||||
const config = await client.getConfig();
|
||||
console.log(' Config:', config);
|
||||
} catch (error) {
|
||||
console.log(' Error:', error.message);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Example 3: Generate embeddings
|
||||
console.log('3. Generating embeddings...');
|
||||
try {
|
||||
const texts = ['Hello world', 'Machine learning is amazing'];
|
||||
const embedResult = await client.embed(texts, 'default');
|
||||
console.log(' Dimensions:', embedResult.dimensions);
|
||||
console.log(' Vectors count:', embedResult.vectors.length);
|
||||
console.log(' First vector (first 5 dims):', embedResult.vectors[0]?.slice(0, 5));
|
||||
} catch (error) {
|
||||
console.log(' Error:', error.message);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Example 4: Single message chat
|
||||
console.log('4. Single message chat...');
|
||||
try {
|
||||
const response = await client.chat('What is 2+2?', 'default');
|
||||
console.log(' Response:', response);
|
||||
} catch (error) {
|
||||
console.log(' Error:', error.message);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Example 5: Multi-turn chat
|
||||
console.log('5. Multi-turn chat...');
|
||||
try {
|
||||
const chatResponse = await client.chatMessages([
|
||||
{ role: 'system', content: 'You are a helpful math tutor.' },
|
||||
{ role: 'user', content: 'What is the Pythagorean theorem?' }
|
||||
], 'default');
|
||||
console.log(' Content:', chatResponse.content);
|
||||
console.log(' Model:', chatResponse.model);
|
||||
console.log(' Usage:', chatResponse.usage);
|
||||
} catch (error) {
|
||||
console.log(' Error:', error.message);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
console.log('=== Examples completed ===');
|
||||
}
|
||||
|
||||
// Run examples
|
||||
main().catch(console.error);
|
||||
3854
ccw/package-lock.json
generated
3854
ccw/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"name": "ccw",
|
||||
"version": "6.2.0",
|
||||
"description": "Claude Code Workflow CLI - Dashboard viewer for workflow sessions and reviews",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"bin": {
|
||||
"ccw": "./bin/ccw.js",
|
||||
"ccw-mcp": "./bin/ccw-mcp.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsx watch src/cli.ts",
|
||||
"test": "node --test tests/*.test.js",
|
||||
"test:codexlens": "node --test tests/codex-lens*.test.js",
|
||||
"test:mcp": "node --test tests/mcp-server.test.js",
|
||||
"lint": "eslint src/"
|
||||
},
|
||||
"keywords": [
|
||||
"claude",
|
||||
"workflow",
|
||||
"cli",
|
||||
"dashboard",
|
||||
"code-review"
|
||||
],
|
||||
"author": "Claude Code Workflow",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.4",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"boxen": "^7.1.0",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^11.0.0",
|
||||
"figlet": "^1.7.0",
|
||||
"glob": "^10.3.0",
|
||||
"gradient-string": "^2.0.2",
|
||||
"inquirer": "^9.2.0",
|
||||
"open": "^9.1.0",
|
||||
"ora": "^7.0.0",
|
||||
"zod": "^4.1.13"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"dist/",
|
||||
"src/",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/claude-code-workflow/ccw"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.12",
|
||||
"@types/gradient-string": "^1.1.6",
|
||||
"@types/inquirer": "^9.0.9",
|
||||
"@types/node": "^25.0.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { cliCommand } from './commands/cli.js';
|
||||
import { memoryCommand } from './commands/memory.js';
|
||||
import { coreMemoryCommand } from './commands/core-memory.js';
|
||||
import { hookCommand } from './commands/hook.js';
|
||||
import { issueCommand } from './commands/issue.js';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
@@ -28,20 +29,32 @@ interface PackageInfo {
|
||||
|
||||
/**
|
||||
* Load package.json with error handling
|
||||
* Tries root package.json first (../../package.json from dist),
|
||||
* then falls back to ccw package.json (../package.json from dist)
|
||||
* @returns Package info with version
|
||||
*/
|
||||
function loadPackageInfo(): PackageInfo {
|
||||
const pkgPath = join(__dirname, '../package.json');
|
||||
// First try root package.json (parent of ccw directory)
|
||||
const rootPkgPath = join(__dirname, '../../package.json');
|
||||
// Fallback to ccw package.json
|
||||
const ccwPkgPath = join(__dirname, '../package.json');
|
||||
|
||||
try {
|
||||
if (!existsSync(pkgPath)) {
|
||||
console.error('Fatal Error: package.json not found.');
|
||||
console.error(`Expected location: ${pkgPath}`);
|
||||
process.exit(1);
|
||||
// Try root package.json first
|
||||
if (existsSync(rootPkgPath)) {
|
||||
const content = readFileSync(rootPkgPath, 'utf8');
|
||||
return JSON.parse(content) as PackageInfo;
|
||||
}
|
||||
|
||||
const content = readFileSync(pkgPath, 'utf8');
|
||||
return JSON.parse(content) as PackageInfo;
|
||||
// Fallback to ccw package.json
|
||||
if (existsSync(ccwPkgPath)) {
|
||||
const content = readFileSync(ccwPkgPath, 'utf8');
|
||||
return JSON.parse(content) as PackageInfo;
|
||||
}
|
||||
|
||||
console.error('Fatal Error: package.json not found.');
|
||||
console.error(`Tried locations:\n - ${rootPkgPath}\n - ${ccwPkgPath}`);
|
||||
process.exit(1);
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
console.error('Fatal Error: package.json contains invalid JSON.');
|
||||
@@ -162,20 +175,28 @@ export function run(argv: string[]): void {
|
||||
.option('--cd <path>', 'Working directory')
|
||||
.option('--includeDirs <dirs>', 'Additional directories (--include-directories for gemini/qwen, --add-dir for codex/claude)')
|
||||
.option('--timeout <ms>', 'Timeout in milliseconds', '300000')
|
||||
.option('--no-stream', 'Disable streaming output')
|
||||
.option('--stream', 'Enable streaming output (default: non-streaming with caching)')
|
||||
.option('--limit <n>', 'History limit')
|
||||
.option('--status <status>', 'Filter by status')
|
||||
.option('--category <category>', 'Execution category: user, internal, insight', 'user')
|
||||
.option('--resume [id]', 'Resume previous session (empty=last, or execution ID, or comma-separated IDs for merge)')
|
||||
.option('--id <id>', 'Custom execution ID (e.g., IMPL-001-step1)')
|
||||
.option('--no-native', 'Force prompt concatenation instead of native resume')
|
||||
.option('--cache [items]', 'Cache: comma-separated @patterns and text content')
|
||||
.option('--inject-mode <mode>', 'Inject mode: none, full, progressive (default: codex=full, others=none)')
|
||||
// Storage options
|
||||
.option('--project <path>', 'Project path for storage operations')
|
||||
.option('--force', 'Confirm destructive operations')
|
||||
.option('--cli-history', 'Target CLI history storage')
|
||||
.option('--memory', 'Target memory storage')
|
||||
.option('--cache', 'Target cache storage')
|
||||
.option('--storage-cache', 'Target cache storage')
|
||||
.option('--config', 'Target config storage')
|
||||
// Cache subcommand options
|
||||
.option('--offset <n>', 'Character offset for cache pagination', '0')
|
||||
.option('--output-type <type>', 'Output type: stdout, stderr, both', 'both')
|
||||
.option('--turn <n>', 'Turn number for cache (default: latest)')
|
||||
.option('--raw', 'Raw output only (no formatting)')
|
||||
.option('--final', 'Output final result only with usage hint')
|
||||
.action((subcommand, args, options) => cliCommand(subcommand, args, options));
|
||||
|
||||
// Memory command
|
||||
@@ -240,5 +261,29 @@ export function run(argv: string[]): void {
|
||||
.option('--type <type>', 'Context type: session-start, context')
|
||||
.action((subcommand, args, options) => hookCommand(subcommand, args, options));
|
||||
|
||||
// Issue command - Issue lifecycle management with JSONL task tracking
|
||||
program
|
||||
.command('issue [subcommand] [args...]')
|
||||
.description('Issue lifecycle management with JSONL task tracking')
|
||||
.option('--title <title>', 'Task title')
|
||||
.option('--type <type>', 'Task type: feature, bug, refactor, test, chore, docs')
|
||||
.option('--status <status>', 'Task status')
|
||||
.option('--phase <phase>', 'Execution phase')
|
||||
.option('--description <desc>', 'Task description')
|
||||
.option('--depends-on <ids>', 'Comma-separated dependency task IDs')
|
||||
.option('--delivery-criteria <items>', 'Pipe-separated delivery criteria')
|
||||
.option('--pause-criteria <items>', 'Pipe-separated pause criteria')
|
||||
.option('--executor <type>', 'Executor: agent, codex, gemini, auto')
|
||||
.option('--priority <n>', 'Task priority (1-5)')
|
||||
.option('--format <fmt>', 'Output format: json, markdown')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--force', 'Force operation')
|
||||
// New options for solution/queue management
|
||||
.option('--solution <path>', 'Solution JSON file path')
|
||||
.option('--solution-id <id>', 'Solution ID')
|
||||
.option('--result <json>', 'Execution result JSON')
|
||||
.option('--reason <text>', 'Failure reason')
|
||||
.action((subcommand, args, options) => issueCommand(subcommand, args, options));
|
||||
|
||||
program.parse(argv);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
projectExists,
|
||||
getStorageLocationInstructions
|
||||
} from '../tools/storage-manager.js';
|
||||
import { getHistoryStore } from '../tools/cli-history-store.js';
|
||||
|
||||
// Dashboard notification settings
|
||||
const DASHBOARD_PORT = process.env.CCW_PORT || 3456;
|
||||
@@ -74,10 +75,18 @@ interface CliExecOptions {
|
||||
cd?: string;
|
||||
includeDirs?: string;
|
||||
timeout?: string;
|
||||
noStream?: boolean;
|
||||
stream?: boolean; // Enable streaming (default: false, caches output)
|
||||
resume?: string | boolean; // true = last, string = execution ID, comma-separated for merge
|
||||
id?: string; // Custom execution ID (e.g., IMPL-001-step1)
|
||||
noNative?: boolean; // Force prompt concatenation instead of native resume
|
||||
cache?: string | boolean; // Cache: true = auto from CONTEXT, string = comma-separated patterns/content
|
||||
injectMode?: 'none' | 'full' | 'progressive'; // Inject mode for cached content
|
||||
}
|
||||
|
||||
/** Cache configuration parsed from --cache */
|
||||
interface CacheConfig {
|
||||
patterns?: string[]; // @patterns to pack (items starting with @)
|
||||
content?: string; // Additional text content (items not starting with @)
|
||||
}
|
||||
|
||||
interface HistoryOptions {
|
||||
@@ -91,11 +100,20 @@ interface StorageOptions {
|
||||
project?: string;
|
||||
cliHistory?: boolean;
|
||||
memory?: boolean;
|
||||
cache?: boolean;
|
||||
storageCache?: boolean;
|
||||
config?: boolean;
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
interface OutputViewOptions {
|
||||
offset?: string;
|
||||
limit?: string;
|
||||
outputType?: 'stdout' | 'stderr' | 'both';
|
||||
turn?: string;
|
||||
raw?: boolean;
|
||||
final?: boolean; // Only output final result with usage hint
|
||||
}
|
||||
|
||||
/**
|
||||
* Show storage information and management options
|
||||
*/
|
||||
@@ -173,15 +191,15 @@ async function showStorageInfo(): Promise<void> {
|
||||
* Clean storage
|
||||
*/
|
||||
async function cleanStorage(options: StorageOptions): Promise<void> {
|
||||
const { all, project, force, cliHistory, memory, cache, config } = options;
|
||||
const { all, project, force, cliHistory, memory, storageCache, config } = options;
|
||||
|
||||
// Determine what to clean
|
||||
const cleanTypes = {
|
||||
cliHistory: cliHistory || (!cliHistory && !memory && !cache && !config),
|
||||
memory: memory || (!cliHistory && !memory && !cache && !config),
|
||||
cache: cache || (!cliHistory && !memory && !cache && !config),
|
||||
cliHistory: cliHistory || (!cliHistory && !memory && !storageCache && !config),
|
||||
memory: memory || (!cliHistory && !memory && !storageCache && !config),
|
||||
cache: storageCache || (!cliHistory && !memory && !storageCache && !config),
|
||||
config: config || false, // Config requires explicit flag
|
||||
all: !cliHistory && !memory && !cache && !config
|
||||
all: !cliHistory && !memory && !storageCache && !config
|
||||
};
|
||||
|
||||
if (project) {
|
||||
@@ -279,6 +297,86 @@ function showStorageHelp(): void {
|
||||
console.log();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show cached output for a conversation with pagination
|
||||
*/
|
||||
async function outputAction(conversationId: string | undefined, options: OutputViewOptions): Promise<void> {
|
||||
if (!conversationId) {
|
||||
console.error(chalk.red('Error: Conversation ID is required'));
|
||||
console.error(chalk.gray('Usage: ccw cli output <conversation-id> [--offset N] [--limit N]'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const store = getHistoryStore(process.cwd());
|
||||
const result = store.getCachedOutput(
|
||||
conversationId,
|
||||
options.turn ? parseInt(options.turn) : undefined,
|
||||
{
|
||||
offset: parseInt(options.offset || '0'),
|
||||
limit: parseInt(options.limit || '10000'),
|
||||
outputType: options.outputType || 'both'
|
||||
}
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
console.error(chalk.red(`Error: Execution not found: ${conversationId}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.raw) {
|
||||
// Raw output only (for piping)
|
||||
if (result.stdout) console.log(result.stdout.content);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.final) {
|
||||
// Final result only with usage hint
|
||||
if (result.stdout) {
|
||||
console.log(result.stdout.content);
|
||||
}
|
||||
console.log();
|
||||
console.log(chalk.gray('─'.repeat(60)));
|
||||
console.log(chalk.dim(`Usage: ccw cli output ${conversationId} [options]`));
|
||||
console.log(chalk.dim(' --raw Raw output (no hint)'));
|
||||
console.log(chalk.dim(' --offset <n> Start from byte offset'));
|
||||
console.log(chalk.dim(' --limit <n> Limit output bytes'));
|
||||
console.log(chalk.dim(` --resume ccw cli -p "..." --resume ${conversationId}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Formatted output
|
||||
console.log(chalk.bold.cyan('Execution Output\n'));
|
||||
console.log(` ${chalk.gray('ID:')} ${result.conversationId}`);
|
||||
console.log(` ${chalk.gray('Turn:')} ${result.turnNumber}`);
|
||||
console.log(` ${chalk.gray('Cached:')} ${result.cached ? chalk.green('Yes') : chalk.yellow('No')}`);
|
||||
console.log(` ${chalk.gray('Status:')} ${result.status}`);
|
||||
console.log(` ${chalk.gray('Time:')} ${result.timestamp}`);
|
||||
console.log();
|
||||
|
||||
if (result.stdout) {
|
||||
console.log(` ${chalk.gray('Stdout:')} (${result.stdout.totalBytes} bytes, offset ${result.stdout.offset})`);
|
||||
console.log(chalk.gray(' ' + '-'.repeat(60)));
|
||||
console.log(result.stdout.content);
|
||||
console.log(chalk.gray(' ' + '-'.repeat(60)));
|
||||
if (result.stdout.hasMore) {
|
||||
console.log(chalk.yellow(` ... ${result.stdout.totalBytes - result.stdout.offset - result.stdout.content.length} more bytes available`));
|
||||
console.log(chalk.gray(` Use --offset ${result.stdout.offset + result.stdout.content.length} to continue`));
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
if (result.stderr && result.stderr.content) {
|
||||
console.log(` ${chalk.gray('Stderr:')} (${result.stderr.totalBytes} bytes, offset ${result.stderr.offset})`);
|
||||
console.log(chalk.gray(' ' + '-'.repeat(60)));
|
||||
console.log(result.stderr.content);
|
||||
console.log(chalk.gray(' ' + '-'.repeat(60)));
|
||||
if (result.stderr.hasMore) {
|
||||
console.log(chalk.yellow(` ... ${result.stderr.totalBytes - result.stderr.offset - result.stderr.content.length} more bytes available`));
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test endpoint for debugging multi-line prompt parsing
|
||||
* Shows exactly how Commander.js parsed the arguments
|
||||
@@ -383,7 +481,7 @@ async function statusAction(): Promise<void> {
|
||||
* @param {Object} options - CLI options
|
||||
*/
|
||||
async function execAction(positionalPrompt: string | undefined, options: CliExecOptions): Promise<void> {
|
||||
const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, timeout, noStream, resume, id, noNative } = options;
|
||||
const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, timeout, stream, resume, id, noNative, cache, injectMode } = options;
|
||||
|
||||
// Priority: 1. --file, 2. --prompt/-p option, 3. positional argument
|
||||
let finalPrompt: string | undefined;
|
||||
@@ -421,6 +519,128 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
|
||||
|
||||
const prompt_to_use = finalPrompt || '';
|
||||
|
||||
// Handle cache option: pack @patterns and/or content
|
||||
let cacheSessionId: string | undefined;
|
||||
let actualPrompt = prompt_to_use;
|
||||
|
||||
if (cache) {
|
||||
const { handler: contextCacheHandler } = await import('../tools/context-cache.js');
|
||||
|
||||
// Parse cache config from comma-separated string
|
||||
// Items starting with @ are patterns, others are text content
|
||||
let cacheConfig: CacheConfig = {};
|
||||
|
||||
if (cache === true) {
|
||||
// --cache without value: auto-extract from CONTEXT field
|
||||
const contextMatch = prompt_to_use.match(/CONTEXT:\s*([^\n]+)/i);
|
||||
if (contextMatch) {
|
||||
const contextLine = contextMatch[1];
|
||||
const patternMatches = contextLine.matchAll(/@[^\s|]+/g);
|
||||
cacheConfig.patterns = Array.from(patternMatches).map(m => m[0]);
|
||||
}
|
||||
} else if (typeof cache === 'string') {
|
||||
// Parse comma-separated items: @patterns and text content
|
||||
const items = cache.split(',').map(s => s.trim()).filter(Boolean);
|
||||
const patterns: string[] = [];
|
||||
const contentParts: string[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
if (item.startsWith('@')) {
|
||||
patterns.push(item);
|
||||
} else {
|
||||
contentParts.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (patterns.length > 0) {
|
||||
cacheConfig.patterns = patterns;
|
||||
}
|
||||
if (contentParts.length > 0) {
|
||||
cacheConfig.content = contentParts.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
// Also extract patterns from CONTEXT if not provided
|
||||
if ((!cacheConfig.patterns || cacheConfig.patterns.length === 0) && prompt_to_use) {
|
||||
const contextMatch = prompt_to_use.match(/CONTEXT:\s*([^\n]+)/i);
|
||||
if (contextMatch) {
|
||||
const contextLine = contextMatch[1];
|
||||
const patternMatches = contextLine.matchAll(/@[^\s|]+/g);
|
||||
cacheConfig.patterns = Array.from(patternMatches).map(m => m[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Pack if we have patterns or content
|
||||
if ((cacheConfig.patterns && cacheConfig.patterns.length > 0) || cacheConfig.content) {
|
||||
const patternCount = cacheConfig.patterns?.length || 0;
|
||||
const hasContent = !!cacheConfig.content;
|
||||
console.log(chalk.gray(` Caching: ${patternCount} pattern(s)${hasContent ? ' + text content' : ''}...`));
|
||||
|
||||
const cacheResult = await contextCacheHandler({
|
||||
operation: 'pack',
|
||||
patterns: cacheConfig.patterns,
|
||||
content: cacheConfig.content,
|
||||
cwd: cd || process.cwd(),
|
||||
include_dirs: includeDirs ? includeDirs.split(',') : undefined,
|
||||
});
|
||||
|
||||
if (cacheResult.success && cacheResult.result) {
|
||||
const packResult = cacheResult.result as { session_id: string; files_packed: number; total_bytes: number };
|
||||
cacheSessionId = packResult.session_id;
|
||||
console.log(chalk.gray(` Cached: ${packResult.files_packed} files, ${packResult.total_bytes} bytes`));
|
||||
console.log(chalk.gray(` Session: ${cacheSessionId}`));
|
||||
|
||||
// Determine inject mode:
|
||||
// --inject-mode explicitly set > tool default (codex=full, others=none)
|
||||
const effectiveInjectMode = injectMode ?? (tool === 'codex' ? 'full' : 'none');
|
||||
|
||||
if (effectiveInjectMode !== 'none' && cacheSessionId) {
|
||||
if (effectiveInjectMode === 'full') {
|
||||
// Read full cache content
|
||||
const readResult = await contextCacheHandler({
|
||||
operation: 'read',
|
||||
session_id: cacheSessionId,
|
||||
offset: 0,
|
||||
limit: 1024 * 1024, // 1MB max
|
||||
});
|
||||
|
||||
if (readResult.success && readResult.result) {
|
||||
const { content: cachedContent, total_bytes } = readResult.result as { content: string; total_bytes: number };
|
||||
console.log(chalk.gray(` Injecting ${total_bytes} bytes (full mode)...`));
|
||||
actualPrompt = `=== CACHED CONTEXT (${packResult.files_packed} files) ===\n${cachedContent}\n\n=== USER PROMPT ===\n${prompt_to_use}`;
|
||||
}
|
||||
} else if (effectiveInjectMode === 'progressive') {
|
||||
// Progressive mode: read first page only (64KB default)
|
||||
const pageLimit = 65536;
|
||||
const readResult = await contextCacheHandler({
|
||||
operation: 'read',
|
||||
session_id: cacheSessionId,
|
||||
offset: 0,
|
||||
limit: pageLimit,
|
||||
});
|
||||
|
||||
if (readResult.success && readResult.result) {
|
||||
const { content: cachedContent, total_bytes, has_more, next_offset } = readResult.result as {
|
||||
content: string; total_bytes: number; has_more: boolean; next_offset: number | null
|
||||
};
|
||||
console.log(chalk.gray(` Injecting ${cachedContent.length}/${total_bytes} bytes (progressive mode)...`));
|
||||
|
||||
const moreInfo = has_more
|
||||
? `\n[... ${total_bytes - cachedContent.length} more bytes available via: context_cache(operation="read", session_id="${cacheSessionId}", offset=${next_offset}) ...]`
|
||||
: '';
|
||||
|
||||
actualPrompt = `=== CACHED CONTEXT (${packResult.files_packed} files, progressive) ===\n${cachedContent}${moreInfo}\n\n=== USER PROMPT ===\n${prompt_to_use}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
} else {
|
||||
console.log(chalk.yellow(` Cache warning: ${cacheResult.error}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse resume IDs for merge scenario
|
||||
const resumeIds = resume && typeof resume === 'string' ? resume.split(',').map(s => s.trim()).filter(Boolean) : [];
|
||||
const isMerge = resumeIds.length > 1;
|
||||
@@ -454,15 +674,15 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
|
||||
custom_id: id || null
|
||||
});
|
||||
|
||||
// Streaming output handler
|
||||
const onOutput = noStream ? null : (chunk: any) => {
|
||||
// Streaming output handler - only active when --stream flag is passed
|
||||
const onOutput = stream ? (chunk: any) => {
|
||||
process.stdout.write(chunk.data);
|
||||
};
|
||||
} : null;
|
||||
|
||||
try {
|
||||
const result = await cliExecutorTool.execute({
|
||||
tool,
|
||||
prompt: prompt_to_use,
|
||||
prompt: actualPrompt,
|
||||
mode,
|
||||
model,
|
||||
cd,
|
||||
@@ -470,11 +690,12 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
|
||||
timeout: timeout ? parseInt(timeout, 10) : 300000,
|
||||
resume,
|
||||
id, // custom execution ID
|
||||
noNative
|
||||
noNative,
|
||||
stream: !!stream // stream=true → streaming enabled, stream=false/undefined → cache output
|
||||
}, onOutput);
|
||||
|
||||
// If not streaming, print output now
|
||||
if (noStream && result.stdout) {
|
||||
// If not streaming (default), print output now
|
||||
if (!stream && result.stdout) {
|
||||
console.log(result.stdout);
|
||||
}
|
||||
|
||||
@@ -497,6 +718,9 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
|
||||
console.log(chalk.gray(` Total: ${result.conversation.turn_count} turns, ${(result.conversation.total_duration_ms / 1000).toFixed(1)}s`));
|
||||
}
|
||||
console.log(chalk.dim(` Continue: ccw cli -p "..." --resume ${result.execution.id}`));
|
||||
if (!stream) {
|
||||
console.log(chalk.dim(` Output (optional): ccw cli output ${result.execution.id}`));
|
||||
}
|
||||
|
||||
// Notify dashboard: execution completed
|
||||
notifyDashboard({
|
||||
@@ -556,14 +780,25 @@ async function historyAction(options: HistoryOptions): Promise<void> {
|
||||
|
||||
console.log(chalk.bold.cyan('\n CLI Execution History\n'));
|
||||
|
||||
const history = await getExecutionHistoryAsync(process.cwd(), { limit: parseInt(limit, 10), tool, status });
|
||||
// Use recursive: true to aggregate history from parent and child projects (matches Dashboard behavior)
|
||||
const history = await getExecutionHistoryAsync(process.cwd(), { limit: parseInt(limit, 10), tool, status, recursive: true });
|
||||
|
||||
if (history.executions.length === 0) {
|
||||
console.log(chalk.gray(' No executions found.\n'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(chalk.gray(` Total executions: ${history.total}\n`));
|
||||
// Count by tool
|
||||
const toolCounts: Record<string, number> = {};
|
||||
for (const exec of history.executions) {
|
||||
toolCounts[exec.tool] = (toolCounts[exec.tool] || 0) + 1;
|
||||
}
|
||||
const toolSummary = Object.entries(toolCounts).map(([t, c]) => `${t}:${c}`).join(' ');
|
||||
|
||||
// Compact table header with tool breakdown
|
||||
console.log(chalk.gray(` Total: ${history.total} | Showing: ${history.executions.length} (${toolSummary})\n`));
|
||||
console.log(chalk.gray(' Status Tool Time Duration ID'));
|
||||
console.log(chalk.gray(' ' + '─'.repeat(70)));
|
||||
|
||||
for (const exec of history.executions) {
|
||||
const statusIcon = exec.status === 'success' ? chalk.green('●') :
|
||||
@@ -573,13 +808,21 @@ async function historyAction(options: HistoryOptions): Promise<void> {
|
||||
: `${exec.duration_ms}ms`;
|
||||
|
||||
const timeAgo = getTimeAgo(new Date(exec.updated_at || exec.timestamp));
|
||||
const turnInfo = exec.turn_count && exec.turn_count > 1 ? chalk.cyan(` [${exec.turn_count} turns]`) : '';
|
||||
const turnInfo = exec.turn_count && exec.turn_count > 1 ? chalk.cyan(`[${exec.turn_count}t]`) : ' ';
|
||||
|
||||
console.log(` ${statusIcon} ${chalk.bold.white(exec.tool.padEnd(8))} ${chalk.gray(timeAgo.padEnd(12))} ${chalk.gray(duration.padEnd(8))}${turnInfo}`);
|
||||
console.log(chalk.gray(` ${exec.prompt_preview}`));
|
||||
console.log(chalk.dim(` ID: ${exec.id}`));
|
||||
console.log();
|
||||
// Compact format: status tool time duration [turns] + id on same line (no truncation)
|
||||
// Truncate prompt preview to 50 chars for compact display
|
||||
const shortPrompt = exec.prompt_preview.replace(/\n/g, ' ').substring(0, 50).trim();
|
||||
console.log(` ${statusIcon} ${chalk.bold.white(exec.tool.padEnd(8))} ${chalk.gray(timeAgo.padEnd(11))} ${chalk.gray(duration.padEnd(8))} ${turnInfo} ${chalk.dim(exec.id)}`);
|
||||
console.log(chalk.gray(` ${shortPrompt}${exec.prompt_preview.length > 50 ? '...' : ''}`));
|
||||
}
|
||||
|
||||
// Usage hint
|
||||
console.log();
|
||||
console.log(chalk.gray(' ' + '─'.repeat(70)));
|
||||
console.log(chalk.dim(' Filter: ccw cli history --tool <gemini|codex|qwen> --limit <n>'));
|
||||
console.log(chalk.dim(' Output: ccw cli output <id> --final'));
|
||||
console.log();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -685,6 +928,10 @@ export async function cliCommand(
|
||||
await storageAction(argsArray[0], options as unknown as StorageOptions);
|
||||
break;
|
||||
|
||||
case 'output':
|
||||
await outputAction(argsArray[0], options as unknown as OutputViewOptions);
|
||||
break;
|
||||
|
||||
case 'test-parse':
|
||||
// Test endpoint to debug multi-line prompt parsing
|
||||
testParseAction(argsArray, options as CliExecOptions);
|
||||
@@ -715,6 +962,7 @@ export async function cliCommand(
|
||||
console.log(chalk.gray(' storage [cmd] Manage CCW storage (info/clean/config)'));
|
||||
console.log(chalk.gray(' history Show execution history'));
|
||||
console.log(chalk.gray(' detail <id> Show execution detail'));
|
||||
console.log(chalk.gray(' output <id> Show execution output with pagination'));
|
||||
console.log(chalk.gray(' test-parse [args] Debug CLI argument parsing'));
|
||||
console.log();
|
||||
console.log(' Options:');
|
||||
@@ -725,14 +973,35 @@ export async function cliCommand(
|
||||
console.log(chalk.gray(' --model <model> Model override'));
|
||||
console.log(chalk.gray(' --cd <path> Working directory'));
|
||||
console.log(chalk.gray(' --includeDirs <dirs> Additional directories'));
|
||||
console.log(chalk.gray(' --timeout <ms> Timeout (default: 300000)'));
|
||||
console.log(chalk.gray(' --timeout <ms> Timeout (default: 0=disabled)'));
|
||||
console.log(chalk.gray(' --resume [id] Resume previous session'));
|
||||
console.log(chalk.gray(' --cache <items> Cache: comma-separated @patterns and text'));
|
||||
console.log(chalk.gray(' --inject-mode <m> Inject mode: none, full, progressive'));
|
||||
console.log();
|
||||
console.log(' Cache format:');
|
||||
console.log(chalk.gray(' --cache "@src/**/*.ts,@CLAUDE.md" # @patterns to pack'));
|
||||
console.log(chalk.gray(' --cache "@src/**/*,extra context" # patterns + text content'));
|
||||
console.log(chalk.gray(' --cache # auto from CONTEXT field'));
|
||||
console.log();
|
||||
console.log(' Inject modes:');
|
||||
console.log(chalk.gray(' none: cache only, no injection (default for gemini/qwen)'));
|
||||
console.log(chalk.gray(' full: inject all cached content (default for codex)'));
|
||||
console.log(chalk.gray(' progressive: inject first 64KB with MCP continuation hint'));
|
||||
console.log();
|
||||
console.log(' Output options (ccw cli output <id>):');
|
||||
console.log(chalk.gray(' --final Final result only with usage hint'));
|
||||
console.log(chalk.gray(' --raw Raw output only (no formatting, for piping)'));
|
||||
console.log(chalk.gray(' --offset <n> Start from byte offset'));
|
||||
console.log(chalk.gray(' --limit <n> Limit output bytes'));
|
||||
console.log();
|
||||
console.log(' Examples:');
|
||||
console.log(chalk.gray(' ccw cli -p "Analyze auth module" --tool gemini'));
|
||||
console.log(chalk.gray(' ccw cli -f prompt.txt --tool codex --mode write'));
|
||||
console.log(chalk.gray(' ccw cli -p "$(cat template.md)" --tool gemini'));
|
||||
console.log(chalk.gray(' ccw cli --resume --tool gemini'));
|
||||
console.log(chalk.gray(' ccw cli -p "..." --cache "@src/**/*.ts" --tool codex'));
|
||||
console.log(chalk.gray(' ccw cli -p "..." --cache "@src/**/*" --inject-mode progressive --tool gemini'));
|
||||
console.log(chalk.gray(' ccw cli output <id> --final # View result with usage hint'));
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import chalk from 'chalk';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
import { homedir } from 'os';
|
||||
|
||||
interface HookOptions {
|
||||
stdin?: boolean;
|
||||
@@ -53,9 +53,10 @@ async function readStdin(): Promise<string> {
|
||||
|
||||
/**
|
||||
* Get session state file path
|
||||
* Uses ~/.claude/.ccw-sessions/ for reliable persistence across sessions
|
||||
*/
|
||||
function getSessionStateFile(sessionId: string): string {
|
||||
const stateDir = join(tmpdir(), '.ccw-sessions');
|
||||
const stateDir = join(homedir(), '.claude', '.ccw-sessions');
|
||||
if (!existsSync(stateDir)) {
|
||||
mkdirSync(stateDir, { recursive: true });
|
||||
}
|
||||
|
||||
1184
ccw/src/commands/issue.ts
Normal file
1184
ccw/src/commands/issue.ts
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user