mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-05 02:30:26 +08:00
## Overview Complete implementation of enterprise-level workflow features including multi-backend execution (Codex/Claude/Gemini), GitHub issue-to-PR automation, hooks system, and comprehensive documentation. ## Major Changes ### 1. Multi-Backend Support (codeagent-wrapper) - Renamed codex-wrapper → codeagent-wrapper - Backend interface with Codex/Claude/Gemini implementations - Multi-format JSON stream parser (auto-detects backend) - CLI flag: --backend codex|claude|gemini (default: codex) - Test coverage: 89.2% **Files:** - codeagent-wrapper/backend.go - Backend interface - codeagent-wrapper/parser.go - Multi-format parser - codeagent-wrapper/config.go - CLI parsing with backend selection - codeagent-wrapper/executor.go - Process execution - codeagent-wrapper/logger.go - Async logging - codeagent-wrapper/utils.go - Utilities ### 2. GitHub Workflow Commands - /gh-create-issue - Create structured issues via guided dialogue - /gh-implement - Issue-to-PR automation with full dev lifecycle **Files:** - github-workflow/commands/gh-create-issue.md - github-workflow/commands/gh-implement.md - skills/codeagent/SKILL.md ### 3. Hooks System - UserPromptSubmit hook for skill activation - Pre-commit example with code quality checks - merge_json operation in install.py for settings.json merging **Files:** - hooks/skill-activation-prompt.sh|.js - hooks/pre-commit.sh - hooks/hooks-config.json - hooks/test-skill-activation.sh ### 4. Skills System - skill-rules.json for auto-activation - codeagent skill for multi-backend wrapper **Files:** - skills/skill-rules.json - skills/codeagent/SKILL.md - skills/codex/SKILL.md (updated) ### 5. Installation System - install.py: Added merge_json operation - config.json: Added "gh" module - config.schema.json: Added op_merge_json schema ### 6. CI/CD - GitHub Actions workflow for testing and building **Files:** - .github/workflows/ci.yml ### 7. Comprehensive Documentation - Architecture overview with ASCII diagrams - Codeagent-wrapper complete usage guide - GitHub workflow detailed examples - Hooks customization guide **Files:** - docs/architecture.md (21KB) - docs/CODEAGENT-WRAPPER.md (9KB) - docs/GITHUB-WORKFLOW.md (9KB) - docs/HOOKS.md (4KB) - docs/enterprise-workflow-ideas.md - README.md (updated with doc links) ## Test Results - All tests passing ✅ - Coverage: 89.2% - Security scan: 0 issues (gosec) ## Breaking Changes - codex-wrapper renamed to codeagent-wrapper - Default backend: codex (documented in README) ## Migration Guide Users with codex-wrapper installed should: 1. Run: python3 install.py --module dev --force 2. Update shell aliases if any 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
198 lines
4.5 KiB
Go
198 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Config holds CLI configuration
|
|
type Config struct {
|
|
Mode string // "new" or "resume"
|
|
Task string
|
|
SessionID string
|
|
WorkDir string
|
|
ExplicitStdin bool
|
|
Timeout int
|
|
Backend string
|
|
}
|
|
|
|
// ParallelConfig defines the JSON schema for parallel execution
|
|
type ParallelConfig struct {
|
|
Tasks []TaskSpec `json:"tasks"`
|
|
}
|
|
|
|
// TaskSpec describes an individual task entry in the parallel config
|
|
type TaskSpec struct {
|
|
ID string `json:"id"`
|
|
Task string `json:"task"`
|
|
WorkDir string `json:"workdir,omitempty"`
|
|
Dependencies []string `json:"dependencies,omitempty"`
|
|
SessionID string `json:"session_id,omitempty"`
|
|
Mode string `json:"-"`
|
|
UseStdin bool `json:"-"`
|
|
}
|
|
|
|
// TaskResult captures the execution outcome of a task
|
|
type TaskResult struct {
|
|
TaskID string `json:"task_id"`
|
|
ExitCode int `json:"exit_code"`
|
|
Message string `json:"message"`
|
|
SessionID string `json:"session_id"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
var backendRegistry = map[string]Backend{
|
|
"codex": CodexBackend{},
|
|
"claude": ClaudeBackend{},
|
|
"gemini": GeminiBackend{},
|
|
}
|
|
|
|
func selectBackend(name string) (Backend, error) {
|
|
key := strings.ToLower(strings.TrimSpace(name))
|
|
if key == "" {
|
|
key = defaultBackendName
|
|
}
|
|
if backend, ok := backendRegistry[key]; ok {
|
|
return backend, nil
|
|
}
|
|
return nil, fmt.Errorf("unsupported backend %q", name)
|
|
}
|
|
|
|
func parseParallelConfig(data []byte) (*ParallelConfig, error) {
|
|
trimmed := bytes.TrimSpace(data)
|
|
if len(trimmed) == 0 {
|
|
return nil, fmt.Errorf("parallel config is empty")
|
|
}
|
|
|
|
tasks := strings.Split(string(trimmed), "---TASK---")
|
|
var cfg ParallelConfig
|
|
seen := make(map[string]struct{})
|
|
|
|
for _, taskBlock := range tasks {
|
|
taskBlock = strings.TrimSpace(taskBlock)
|
|
if taskBlock == "" {
|
|
continue
|
|
}
|
|
|
|
parts := strings.SplitN(taskBlock, "---CONTENT---", 2)
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("task block missing ---CONTENT--- separator")
|
|
}
|
|
|
|
meta := strings.TrimSpace(parts[0])
|
|
content := strings.TrimSpace(parts[1])
|
|
|
|
task := TaskSpec{WorkDir: defaultWorkdir}
|
|
for _, line := range strings.Split(meta, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
kv := strings.SplitN(line, ":", 2)
|
|
if len(kv) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(kv[0])
|
|
value := strings.TrimSpace(kv[1])
|
|
|
|
switch key {
|
|
case "id":
|
|
task.ID = value
|
|
case "workdir":
|
|
task.WorkDir = value
|
|
case "session_id":
|
|
task.SessionID = value
|
|
task.Mode = "resume"
|
|
case "dependencies":
|
|
for _, dep := range strings.Split(value, ",") {
|
|
dep = strings.TrimSpace(dep)
|
|
if dep != "" {
|
|
task.Dependencies = append(task.Dependencies, dep)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if task.ID == "" {
|
|
return nil, fmt.Errorf("task missing id field")
|
|
}
|
|
if content == "" {
|
|
return nil, fmt.Errorf("task %q missing content", task.ID)
|
|
}
|
|
if _, exists := seen[task.ID]; exists {
|
|
return nil, fmt.Errorf("duplicate task id: %s", task.ID)
|
|
}
|
|
|
|
task.Task = content
|
|
cfg.Tasks = append(cfg.Tasks, task)
|
|
seen[task.ID] = struct{}{}
|
|
}
|
|
|
|
if len(cfg.Tasks) == 0 {
|
|
return nil, fmt.Errorf("no tasks found")
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
func parseArgs() (*Config, error) {
|
|
args := os.Args[1:]
|
|
if len(args) == 0 {
|
|
return nil, fmt.Errorf("task required")
|
|
}
|
|
|
|
backendName := defaultBackendName
|
|
filtered := make([]string, 0, len(args))
|
|
for i := 0; i < len(args); i++ {
|
|
arg := args[i]
|
|
switch {
|
|
case arg == "--backend":
|
|
if i+1 >= len(args) {
|
|
return nil, fmt.Errorf("--backend flag requires a value")
|
|
}
|
|
backendName = args[i+1]
|
|
i++
|
|
continue
|
|
case strings.HasPrefix(arg, "--backend="):
|
|
value := strings.TrimPrefix(arg, "--backend=")
|
|
if value == "" {
|
|
return nil, fmt.Errorf("--backend flag requires a value")
|
|
}
|
|
backendName = value
|
|
continue
|
|
}
|
|
filtered = append(filtered, arg)
|
|
}
|
|
|
|
if len(filtered) == 0 {
|
|
return nil, fmt.Errorf("task required")
|
|
}
|
|
args = filtered
|
|
|
|
cfg := &Config{WorkDir: defaultWorkdir, Backend: backendName}
|
|
|
|
if args[0] == "resume" {
|
|
if len(args) < 3 {
|
|
return nil, fmt.Errorf("resume mode requires: resume <session_id> <task>")
|
|
}
|
|
cfg.Mode = "resume"
|
|
cfg.SessionID = args[1]
|
|
cfg.Task = args[2]
|
|
cfg.ExplicitStdin = (args[2] == "-")
|
|
if len(args) > 3 {
|
|
cfg.WorkDir = args[3]
|
|
}
|
|
} else {
|
|
cfg.Mode = "new"
|
|
cfg.Task = args[0]
|
|
cfg.ExplicitStdin = (args[0] == "-")
|
|
if len(args) > 1 {
|
|
cfg.WorkDir = args[1]
|
|
}
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|