diff --git a/.ai-context/architecture-decisions.md b/.ai-context/architecture-decisions.md new file mode 100644 index 0000000..5c27cd2 --- /dev/null +++ b/.ai-context/architecture-decisions.md @@ -0,0 +1,83 @@ +# Architecture Decisions + +> Records counter-intuitive design decisions that AI agents cannot infer from code. +> Only add entries for decisions that would surprise someone reading the code. + +--- + +## ADR-001: LoopAgent max_iterations=1 + +**Decision**: All Actor-Critic loops use `max_iterations=1` + +**Reason**: SequentialAgent has a bug where `exit_loop()` terminates the entire chain, not just the loop. Using `max_iterations=1` lets LoopAgent complete naturally. + +**Impact**: All critical stages (PRD, Design, Plan, Coding) + +**Do not**: Change this parameter or migrate to SequentialAgent without testing + +--- + +## ADR-002: JSON Storage over SQLite + +**Decision**: Use JSON files for persistence instead of SQLite + +**Reason**: Simpler debugging, easy to inspect/edit, no external dependencies. Data volume is small and there's no concurrent access requirement. + +**Impact**: All persistence layer, no migrations needed + +**Limitation**: Not suitable for large-scale concurrent scenarios + +--- + +## ADR-003: Coding Stage has 5 Iterations + +**Decision**: Coding Actor-Critic loop allows `max_iterations=5` while others only 1 + +**Reason**: Code often needs iterative refinement based on test results. Other stages typically complete in one pass. + +**Impact**: CodingStage, coding_loop agent + +--- + +## ADR-004: Two-Step Knowledge Promotion + +**Decision**: Insights → Decisions requires two separate tools (SaveInsightTool + PromoteToDecisionTool) + +**Reason**: Not all insights should become decisions. Human review at promotion step ensures quality. + +**Impact**: Memory tools, knowledge workflow + +--- + +## ADR-005: GotoStage Only Goes Backward + +**Decision**: GotoStageTool can only jump to earlier stages, never forward + +**Reason**: Skipping stages would miss required artifacts. Re-executing earlier stages is safe. + +**Impact**: Pipeline control flow, PM Agent navigation + +--- + +## ADR-006: HITL Timeout Default Pass + +**Decision**: If HITL confirmation times out, default action is "Pass" (continue) + +**Reason**: Better to proceed than to block indefinitely. User can always re-run or use PM Agent to navigate back. + +**Impact**: Stage execution, GUI timeout handling + +--- + +## Adding New ADRs + +When making a design decision that: +- Goes against common patterns +- Has non-obvious rationale +- Would confuse someone reading the code later + +Add an entry with: +- Clear decision statement +- The "why" (most important) +- Impact scope +- Any "do not" warnings diff --git a/.ai-context/core/constraints.md b/.ai-context/core/constraints.md new file mode 100644 index 0000000..70e6204 --- /dev/null +++ b/.ai-context/core/constraints.md @@ -0,0 +1,53 @@ +# Constraints & Boundaries + +## Security Constraints + +| Constraint | Rule | +|------------|------| +| Path Validation | All file ops within workspace | +| Blocked Commands | rm -rf, sudo, chmod 777, mkfs, dd | +| Allowed Commands | cargo, npm, pip, bun, yarn, python, node, git (read-only) | + +## Rate Limiting + +| Resource | Limit | +|----------|-------| +| LLM API | 30 req/min, concurrency=1 | +| Retry | 3 attempts with backoff | + +## HITL Gates + +- Critical stages (prd, design, plan, coding): require confirmation +- Non-critical (idea, check, delivery): auto-proceed +- Max feedback loops: 5 per stage + +| Action | Behavior | +|--------|----------| +| Pass | Continue | +| Feedback | Re-execute stage | +| Cancel | Pause iteration | + +## Tool Limits + +| Constraint | Value | +|------------|-------| +| Max file size | 1MB | +| Max files per list | 100 | + +## Error Handling + +| Type | Behavior | +|------|----------| +| LLM timeout | Retry with backoff | +| Tool failure | Retry up to 3 times | +| Invalid config | Halt, require fix | +| Security violation | Halt, log | + +## Interaction Layer + +| Backend | Location | Use Case | +|---------|----------|----------| +| CliBackend | `interaction/cli.rs` | Terminal + dialoguer | +| TauriBackend | `interaction/tauri.rs` | Event-driven IPC | + +HITL Flow: `request_input([Pass, Feedback, Cancel])` → wait for response diff --git a/.ai-context/core/modules.map b/.ai-context/core/modules.map new file mode 100644 index 0000000..070f316 --- /dev/null +++ b/.ai-context/core/modules.map @@ -0,0 +1,123 @@ +# Module Dependency Map +# Purpose: Quick navigation for AI agents to locate code and understand relationships +# Note: For detailed function signatures, grep the code directly + +## ARCHITECTURE LAYERS + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRESENTATION LAYER │ +│ ┌─────────────────┐ ┌─────────────────────────────┐ │ +│ │ cowork-cli │ │ cowork-gui │ │ +│ │ (clap CLI) │ │ (Tauri + React + Ant D) │ │ +│ └────────┬────────┘ └──────────────┬──────────────┘ │ +└───────────┼──────────────────────────────────┼──────────────────┘ + │ │ + ▼ ▼ +┌───────────────────────────────────────────────────────────────────┐ +│ APPLICATION LAYER │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ InteractiveBackend Trait │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ IterationExecutor │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌───────────────────────────────────────────────────────────────────┐ +│ DOMAIN LAYER (cowork-core) │ +│ Project (Aggregate) │ Iteration (Entity) │ ProjectMemory │ +│ Stage Trait → [Idea, PRD, Design, Plan, Coding, Check, Delivery]│ +└───────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌───────────────────────────────────────────────────────────────────┐ +│ INFRASTRUCTURE LAYER │ +│ LLM Client │ Stores (JSON) │ Tools (30+) │ ACP │ Skills │ +└───────────────────────────────────────────────────────────────────┘ +``` + +## MODULE DEPENDENCY FLOW + +``` +CLI/GUI → InteractiveBackend → IterationExecutor → Pipeline → Stages → Agents → Tools → LLM/Persistence +``` + +## KEY TRAITS + +| Trait | Location | Purpose | +|-------|----------|---------| +| `InteractiveBackend` | `interaction/mod.rs` | CLI/GUI abstraction | +| `Stage` | `pipeline/mod.rs` | Stage execution behavior | +| `Tool` | adk-rust | Tool execution interface | + +## CORE FLOWS + +### Genesis Iteration +``` +create_genesis_iteration() → execute() → stages[idea→delivery] → generate_knowledge() +``` + +### Actor-Critic Loop (critical stages) +``` +Actor generates artifact → Critic validates → HITL confirmation → [Pass|Feedback|Cancel] +``` + +### Evolution Iteration +``` +create_evolution_iteration() → prepare_workspace(inheritance) → inject_knowledge() → stages +``` + +## MODULE FILES REFERENCE + +### Pipeline Module +`crates/cowork-core/src/pipeline/` +- `mod.rs` - Stage trait, stage factory +- `executor/mod.rs` - IterationExecutor (main orchestrator) +- `executor/interaction_ext.rs` - HITL flow +- `stages/*.rs` - Individual stage implementations + +### Domain Module +`crates/cowork-core/src/domain/` +- `project.rs` - Project, ProjectMetadata +- `iteration.rs` - Iteration, Artifacts, InheritanceMode +- `memory.rs` - ProjectMemory, IterationKnowledge + +### Tools Module +`crates/cowork-core/src/tools/` +- `file_tools.rs` - ReadFile, WriteFile, ListFiles +- `data_tools.rs` - Requirement/Feature/Task CRUD +- `hitl_tools.rs` - Review, Feedback +- `memory_tools.rs` - Query, Save, Promote +- `artifact_tools.rs` - Save stage artifacts +- `pm_tools.rs` - PM Agent tools + +### Persistence Module +`crates/cowork-core/src/persistence/` +- `project_store.rs` - ProjectStore +- `iteration_store.rs` - IterationStore +- `memory_store.rs` - MemoryStore + +### Agents Module +`crates/cowork-core/src/agents/` +- `mod.rs` - All agent builders +- `external_coding_agent.rs` - ACP integration + +### Instructions Module +`crates/cowork-core/src/instructions/` +- `*.rs` - Prompts for each stage + +## QUICK NAVIGATION BY TASK + +| Task | Location | +|------|----------| +| Modify pipeline execution | `pipeline/executor/mod.rs` | +| Add new stage | `pipeline/stages/` + register in `mod.rs` | +| Add new tool | `tools/` + re-export in `mod.rs` | +| Modify HITL | `interaction/mod.rs` | +| Add agent instruction | `instructions/` | +| Modify storage | `persistence/` + `domain/` | +| Add CLI command | `cowork-cli/src/commands/` | +| Modify GUI | `cowork-gui/src/` (frontend) or `src-tauri/` (backend) | \ No newline at end of file diff --git a/.ai-context/domains/domain-logic.md b/.ai-context/domains/domain-logic.md new file mode 100644 index 0000000..897f5ea --- /dev/null +++ b/.ai-context/domains/domain-logic.md @@ -0,0 +1,51 @@ +# Domain Logic + +## Core Entities + +| Entity | Location | Purpose | +|--------|----------|---------| +| Project | `domain/project.rs` | Aggregate root, contains Iterations | +| Iteration | `domain/iteration.rs` | Single development cycle | +| ProjectMemory | `domain/memory.rs` | Cross-iteration knowledge | +| IterationKnowledge | `domain/memory.rs` | Single iteration learnings | + +## Relationships + +``` +Project 1:N Iteration +Project 1:1 ProjectMemory +Iteration 1:1 IterationKnowledge +``` + +## Iteration Lifecycle + +``` +Draft → Running → [Paused] → Completed | Failed +``` + +| Status | Operations | +|--------|------------| +| Draft | start, delete | +| Running | pause | +| Paused | continue, delete | +| Completed | create_evolution | +| Failed | retry, delete | + +## Iteration Types + +| Type | Description | +|------|-------------| +| Genesis | Fresh start | +| Evolution | Based on previous iteration | + +## Knowledge Types + +| Type | Scope | +|------|-------| +| Decision | Project-level | +| Pattern | Project-level | +| Issue | Iteration → Project | +| Learning | Iteration-level | + +--- +**Note**: For struct fields, read `domain/*.rs` directly. diff --git a/.ai-context/domains/pipeline.md b/.ai-context/domains/pipeline.md new file mode 100644 index 0000000..dd0f68e --- /dev/null +++ b/.ai-context/domains/pipeline.md @@ -0,0 +1,48 @@ +# Pipeline & Agents Domain + +## 7-Stage Workflow + +| Stage | HITL | Pattern | Output | +|-------|------|---------|--------| +| Idea | No | Simple | idea.md | +| PRD | Yes | Actor-Critic | prd.md | +| Design | Yes | Actor-Critic | design.md | +| Plan | Yes | Actor-Critic | plan.md | +| Coding | Yes | Actor-Critic | workspace/ | +| Check | No | Simple | check_report.md | +| Delivery | No | Simple | delivery_report.md | + +## Execution Flow + +``` +prepare_workspace() → stages[idea→delivery] → generate_knowledge() + ↓ + HITL gate (critical stages) +``` + +## Actor-Critic Pattern + +``` +Actor generates → Critic validates → HITL confirms → [Pass|Feedback loop] +``` + +**Critical**: LoopAgent uses `max_iterations=1` (see ADR-001) + +## Agent Types + +| Type | Stages | Pattern | +|------|--------|---------| +| Simple | Idea, Check, Delivery | Single LlmAgent | +| Actor-Critic | PRD, Design, Plan, Coding | LoopAgent | +| PM Agent | Post-Delivery | Interactive | + +## Code Locations + +| Component | Location | +|-----------|----------| +| Stage trait | `pipeline/mod.rs` | +| Executor | `pipeline/executor/mod.rs` | +| Stages | `pipeline/stages/*.rs` | +| Agent builders | `agents/mod.rs` | +| Instructions | `instructions/*.rs` | +| External agent | `agents/external_coding_agent.rs` | diff --git a/.ai-context/domains/tools.md b/.ai-context/domains/tools.md new file mode 100644 index 0000000..2d6043a --- /dev/null +++ b/.ai-context/domains/tools.md @@ -0,0 +1,28 @@ +# Tools Domain + +## Tool Categories + +| Category | File | Key Tools | +|----------|------|-----------| +| File | `file_tools.rs` | ReadFile, WriteFile, ListFiles | +| Data | `data_tools.rs` | CreateRequirement, AddFeature, GetRequirements, CreateTask | +| HITL | `hitl_tools.rs` | ReviewWithFeedback, ProvideFeedback | +| Memory | `memory_tools.rs` | QueryMemory, SaveInsight, PromoteToDecision | +| Artifact | `artifact_tools.rs` | SaveIdea, SavePrdDoc, SaveDesignDoc | +| Load | `load_artifacts.rs` | LoadIdea, LoadPrdDoc, LoadFeedbackHistory | +| Control | `control_tools.rs`, `goto_stage_tool.rs` | GotoStage | +| Validation | `validation_tools.rs` | CheckFeatureCoverage, CheckTaskDependencies | +| Test/Lint | `test_lint_tools.rs` | RunCommand, CheckTests, CheckLint | +| Knowledge | `knowledge_tools.rs` | LoadBaseKnowledge, SaveKnowledgeSnapshot | +| PM | `pm_tools.rs` | PMGotoStage, PMCreateIteration, PMRespond | +| Deploy | `deployment_tools.rs` | CopyWorkspaceToProject | +| Import | `legacy_project_analyzer_tools.rs` | ImportProject | + +## Security + +- All file ops validated within workspace +- Dangerous commands blocked + +## Location + +`crates/cowork-core/src/tools/` \ No newline at end of file diff --git a/.ai-context/manifest.yaml b/.ai-context/manifest.yaml new file mode 100644 index 0000000..69b34d8 --- /dev/null +++ b/.ai-context/manifest.yaml @@ -0,0 +1,78 @@ +# AI Context Manifest +# Version: 2.1.0 +# Updated: 2026-03-29 +# Project: cowork-forge v2.5.0 + +manifest_version: "2.1" +project: + name: cowork-forge + version: "2.5.0" + language: rust + edition: "2024" + +documents: + - path: project.snapshot + purpose: Project structure, storage, config + tokens: ~800 + + - path: core/modules.map + purpose: Module dependencies, navigation + tokens: ~800 + + - path: core/constraints.md + purpose: Security, limits, HITL rules + tokens: ~400 + + - path: domains/pipeline.md + purpose: Pipeline + Agents + tokens: ~500 + + - path: domains/domain-logic.md + purpose: Core entities, relationships + tokens: ~400 + + - path: domains/tools.md + purpose: Tool categories reference + tokens: ~400 + + - path: architecture-decisions.md + purpose: Non-obvious design decisions + tokens: ~500 + + - path: prompts/coding-context.md + purpose: Code style, common patterns + tokens: ~500 + +usage: + coding_context: + - project.snapshot + - core/modules.map + - core/constraints.md + - domains/pipeline.md + - domains/tools.md + - architecture-decisions.md + + debug_context: + - project.snapshot + - core/constraints.md + - architecture-decisions.md + +total_tokens: ~4300 + +# ============================================================================= +# MAINTENANCE GUIDE +# ============================================================================= +# +# | Code Change | Update File | +# |--------------------------|------------------------| +# | New tool | domains/tools.md | +# | New stage | domains/pipeline.md | +# | New module | core/modules.map | +# | New constraint | core/constraints.md | +# | Architecture decision | architecture-decisions.md | +# | New crate | project.snapshot | +# +# | NO UPDATE: struct fields, function signatures, refactoring | +# +# Command: "Update .ai-context because I [added/changed] [specific thing]" +# ============================================================================= diff --git a/.ai-context/project.snapshot b/.ai-context/project.snapshot new file mode 100644 index 0000000..28a440d --- /dev/null +++ b/.ai-context/project.snapshot @@ -0,0 +1,84 @@ +# Cowork Forge - AI Context Snapshot +# Purpose: Minimal context for AI agents to understand project structure + +## PROJECT OVERVIEW + +name: cowork-forge +type: rust-workspace +edition: "2024" +version: "2.5.0" +description: AI-native iterative software development platform with multi-agent orchestration + +## CRATES STRUCTURE + +### cowork-core +path: crates/cowork-core/ +key_modules: + - pipeline: 7-stage workflow orchestration + - domain: Project, Iteration, Memory aggregates (DDD) + - tools: 30+ ADK tools + - persistence: JSON-based storage + - interaction: CLI/GUI abstraction + - agents: Agent builders + - instructions: Prompts for each stage + - config_definition: data-driven configuration system + +### cowork-cli +path: crates/cowork-cli/ +commands: [init, iter, list, show, continue, status, delete, import, knowledge, config] + +### cowork-gui +path: crates/cowork-gui/ +description: Tauri + React desktop application + +--- + +## STORAGE STRUCTURE + +``` +.cowork-v2/ +├── project.json # Project metadata +├── memory.json # ProjectMemory +└── iterations/ + └── iter--/ + ├── iteration.json # Iteration metadata + ├── memory.json # IterationKnowledge + ├── artifacts/ + │ ├── idea.md, prd.md, design.md, plan.md, delivery_report.md + ├── workspace/ # Generated code + └── summaries/ # Document summaries +``` + +| Store | File | Purpose | +|-------|------|---------| +| ProjectStore | `persistence/project_store.rs` | project.json | +| IterationStore | `persistence/iteration_store.rs` | iterations/* | +| MemoryStore | `persistence/memory_store.rs` | memory.json | + +--- + +## CONFIGURATION + +### LLM Config (config.toml) +Location: Platform-specific app data directory +- Windows: `%APPDATA%/cowork-forge/config.toml` +- macOS: `~/Library/Application Support/cowork-forge/config.toml` +- Linux: `~/.config/cowork-forge/config.toml` + +Fields: llm.api_base_url, llm.api_key, llm.model_name, embedding.* + +### Config Registry +`config_definition/` - Data-driven configuration: +- AgentDefinition: Custom agent config +- StageDefinition: Stage behavior config +- FlowDefinition: Workflow pipeline config + +--- + +## EXTERNAL INTEGRATIONS + +- **ACP**: `acp/` - External coding agent integration (opencode, iflow, codex, etc.) +- **Skills**: `skills/` - Domain-specific capability injection (agentskills.io) + +--- +**Note**: For detailed implementations, see corresponding source files. diff --git a/.ai-context/prompts/coding-context.md b/.ai-context/prompts/coding-context.md new file mode 100644 index 0000000..53544e4 --- /dev/null +++ b/.ai-context/prompts/coding-context.md @@ -0,0 +1,60 @@ +# Coding Task Context + +## Code Style + +### Rust Conventions +- Use `anyhow::Result` for error handling +- Use `thiserror` for custom error types in domain +- Prefer `async_trait` for trait methods +- snake_case functions, PascalCase types + +### File Organization +- One module per file when substantial +- `mod.rs` for re-exports +- Tests in same file with `#[cfg(test)]` + +--- + +## Common Task Patterns + +### Adding a New Tool +1. Create in `tools/*.rs` +2. Implement `adk_tool::Tool` trait +3. Re-export in `tools/mod.rs` +4. Register in agent builder + +### Adding a New Stage +1. Create in `pipeline/stages/*.rs` +2. Implement `Stage` trait +3. Register in `get_all_stages()` and `create_stage_by_id()` +4. Add to `is_critical_stage()` if HITL needed + +### Modifying HITL +1. Update `InteractiveBackend` trait if needed +2. Implement in CliBackend and TauriBackend +3. Use in `pipeline/executor/interaction_ext.rs` + +### Adding Domain Entity +1. Define in `domain/*.rs` +2. Add to `mod.rs` re-exports +3. Update persistence if new storage needed + +--- + +## Security Reminders + +- All file operations within workspace +- No shell commands outside allowed list +- Never expose API keys in logs + +--- + +## Quick Reference + +| Need | File | +|------|------| +| Stage execution | `pipeline/executor/mod.rs` | +| Tools | `tools/mod.rs` | +| Domain entities | `domain/mod.rs` | +| HITL | `interaction/mod.rs` | +| Instructions | `instructions/mod.rs` | \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0e779bd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,135 @@ +# Cowork Forge - Agent Context + +> AGENTS.md is the README for AI coding agents. This file provides project context and instructions. + +--- + +## Project Overview + +**Cowork Forge** is an AI-native iterative software development platform with multi-agent orchestration. + +### Technology Stack +- **Language**: Rust (edition 2024) +- **Agent Framework**: adk-rust +- **GUI**: Tauri + React + Ant Design +- **Architecture**: Hexagonal + DDD + +### Key Directories +``` +crates/ +├── cowork-core/ # Domain logic, pipeline, tools (MAIN) +├── cowork-cli/ # CLI adapter +└── cowork-gui/ # Tauri + React GUI +``` + +--- + +## Setup Commands + +```bash +cargo build +cargo test +cargo run --package cowork-cli -- +cd crates/cowork-gui && cargo tauri dev +``` + +--- + +## Context Files + +Load `.ai-context/` based on task type: + +### For Coding Tasks +``` +Read: .ai-context/project.snapshot +Read: .ai-context/core/modules.map +Read: .ai-context/core/constraints.md +Read: .ai-context/domains/tools.md (if working with tools) +Read: .ai-context/domains/pipeline.md (if working with pipeline) +Read: .ai-context/architecture-decisions.md +``` + +### For Debugging +``` +Read: .ai-context/project.snapshot +Read: .ai-context/core/constraints.md +Read: .ai-context/architecture-decisions.md +``` + +--- + +## Code Style + +- Use `anyhow::Result` for error handling +- No `unwrap()` in production code +- Use `async_trait` for async trait methods +- snake_case for functions, PascalCase for types + +--- + +## Key Files + +| Task | File | +|------|------| +| Pipeline execution | `crates/cowork-core/src/pipeline/executor/mod.rs` | +| Stage implementations | `crates/cowork-core/src/pipeline/stages/*.rs` | +| Tool implementations | `crates/cowork-core/src/tools/*.rs` | +| Domain entities | `crates/cowork-core/src/domain/*.rs` | +| HITL interface | `crates/cowork-core/src/interaction/mod.rs` | + +--- + +## Testing + +```bash +cargo test +cargo test -p cowork-core +``` + +--- + +## Updating AI Context + +### When Agent Should Remind User + +At start of coding session, check recent changes: +```bash +git diff --name-only HEAD~10 +``` + +If changed files include: +- `crates/cowork-core/src/tools/*.rs` → Remind: "Tools changed, update tools.md?" +- `crates/cowork-core/src/pipeline/stages/*.rs` → Remind: "Stages changed, update pipeline.md?" +- `crates/cowork-core/src/domain/*.rs` → Remind: "Domain entities changed, update domain-logic.md?" + +### Manual Update Command + +When making significant changes, ask the agent to update context: + +``` +"Update .ai-context because I [added/changed] [specific thing]" +``` + +Examples: +- "Update .ai-context because I added a new tool" +- "Update .ai-context because I added a new stage" +- "Update .ai-context because I made an architecture decision" + +The agent will read `manifest.yaml` for maintenance guide. + +**No update needed for**: struct fields, function signatures, refactoring. + +--- + +## Context Files Index + +| File | Purpose | +|------|---------| +| `project.snapshot` | Project structure, storage, core concepts | +| `core/modules.map` | Module dependencies, navigation | +| `core/constraints.md` | Security, rate limits, HITL, interaction | +| `domains/pipeline.md` | Pipeline + Agents | +| `domains/domain-logic.md` | Core entities and relationships | +| `domains/tools.md` | Tool ecosystem | +| `architecture-decisions.md` | Non-obvious design decisions | +| `prompts/coding-context.md` | Code style and patterns | \ No newline at end of file diff --git a/README_zh.md b/README_zh.md index f1b6ad5..19833d9 100644 --- a/README_zh.md +++ b/README_zh.md @@ -263,7 +263,7 @@ Cowork Forge 的专业智能体像真实开发团队一样协同工作: ## Agents Team配置系统 -Cowork Forge V3 引入了**数据驱动的配置系统**,将原本硬编码的 Agent、Stage、Flow、Skill 和 Integration 定义转为可配置的 JSON 格式,无需修改代码即可实现前所未有的灵活性。 +Cowork Forge 引入了**数据驱动的配置系统**,将原本硬编码的 Agent、Stage、Flow、Skill 和 Integration 定义转为可配置的 JSON 格式,无需修改代码即可实现前所未有的灵活性。 ### 自定义工作流程 (Flow) diff --git a/crates/cowork-core/src/config_definition/agent_factory.rs b/crates/cowork-core/src/config_definition/agent_factory.rs index 92b4fdb..009c021 100644 --- a/crates/cowork-core/src/config_definition/agent_factory.rs +++ b/crates/cowork-core/src/config_definition/agent_factory.rs @@ -11,7 +11,7 @@ use crate::config_definition::{ }; use crate::instructions::*; use crate::tools::*; -use crate::storage::set_iteration_id; +use crate::persistence::set_iteration_id; use crate::skills::{SkillManager, SelectionPolicy}; use adk_agent::{LlmAgentBuilder, LoopAgent}; use adk_core::{Llm, Agent, IncludeContents}; diff --git a/crates/cowork-core/src/data/mod.rs b/crates/cowork-core/src/data/mod.rs index 40ba01b..75d7615 100644 --- a/crates/cowork-core/src/data/mod.rs +++ b/crates/cowork-core/src/data/mod.rs @@ -1,4 +1,3 @@ // Data models module pub mod models; -pub mod schemas; pub use models::*; diff --git a/crates/cowork-core/src/data/schemas/mod.rs b/crates/cowork-core/src/data/schemas/mod.rs deleted file mode 100644 index b00c300..0000000 --- a/crates/cowork-core/src/data/schemas/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Validation utilities for data schemas -// This module provides validation logic for structured data \ No newline at end of file diff --git a/crates/cowork-core/src/data/schemas/validation.rs b/crates/cowork-core/src/data/schemas/validation.rs deleted file mode 100644 index b00c300..0000000 --- a/crates/cowork-core/src/data/schemas/validation.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Validation utilities for data schemas -// This module provides validation logic for structured data \ No newline at end of file diff --git a/crates/cowork-core/src/lib.rs b/crates/cowork-core/src/lib.rs index e803e9f..4cfa688 100644 --- a/crates/cowork-core/src/lib.rs +++ b/crates/cowork-core/src/lib.rs @@ -13,9 +13,8 @@ pub mod acp; pub mod domain; pub mod persistence; -// Data models and storage +// Data models pub mod data; -pub mod storage; // Tech stack configuration pub mod tech_stack; @@ -36,7 +35,7 @@ pub mod interaction; // Skills ecosystem (agentskills.io standard via adk-skill) pub mod skills; -// Integration system (V3) +// Integration system pub mod integration; // Project importer for legacy projects @@ -46,7 +45,6 @@ pub mod importer; pub use domain::*; pub use persistence::*; pub use data::*; -pub use storage::*; pub use llm::*; pub use agents::{create_project_manager_agent, execute_pm_agent_message, execute_pm_agent_message_streaming, PMAgentResult, PMAgentAction, PMAgentStreamCallback, create_legacy_project_analyzer, create_legacy_project_analyzer_with_context}; pub use tech_stack::*; @@ -69,7 +67,7 @@ pub use config::{get_system_locale, set_system_locale, get_language_instruction} // Re-exports for ACP module pub use acp::{AcpClient, AcpTaskResult}; -// Re-exports for config_definition (V3) +// Re-exports for config_definition pub use config_definition::{ AgentDefinition, AgentType, ModelConfig, ToolReference, IncludeContentsMode, StageDefinition, StageType, HookConfig, HookPoint, ArtifactConfig, StageRetryConfig, @@ -97,7 +95,7 @@ pub use skills::{ SkillError, SkillResult, }; -// Re-exports for integration (V3) +// Re-exports for integration pub use integration::{ HookManager, HookExecutionContext, HookExecutionResult, IntegrationAdapter, AdapterError, RestAdapter, diff --git a/crates/cowork-core/src/storage/mod.rs b/crates/cowork-core/src/persistence/iteration_data.rs similarity index 98% rename from crates/cowork-core/src/storage/mod.rs rename to crates/cowork-core/src/persistence/iteration_data.rs index c868944..137ef81 100644 --- a/crates/cowork-core/src/storage/mod.rs +++ b/crates/cowork-core/src/persistence/iteration_data.rs @@ -1,12 +1,14 @@ -// Storage layer for .cowork-v2/iterations/{iteration_id}/ directory +// Iteration-specific data storage +// Handles data, artifacts, and session files within .cowork-v2/iterations/{iteration_id}/ + use crate::data::*; -use crate::persistence::get_cowork_dir; +use super::get_cowork_dir; use anyhow::{Context, Result}; use std::fs; use std::path::PathBuf; use std::sync::Mutex; -// Thread-local storage for current iteration ID +// Global static storage for current iteration ID static CURRENT_ITERATION_ID: Mutex> = Mutex::new(None); /// Set the current iteration ID for data operations diff --git a/crates/cowork-core/src/persistence/mod.rs b/crates/cowork-core/src/persistence/mod.rs index 9fee0c4..ccbe4a7 100644 --- a/crates/cowork-core/src/persistence/mod.rs +++ b/crates/cowork-core/src/persistence/mod.rs @@ -5,10 +5,12 @@ use std::sync::{Mutex, OnceLock}; pub mod iteration_store; pub mod memory_store; pub mod project_store; +pub mod iteration_data; pub use iteration_store::*; pub use memory_store::*; pub use project_store::*; +pub use iteration_data::*; const COWORK_DIR: &str = ".cowork-v2"; diff --git a/crates/cowork-core/src/pipeline/executor/knowledge.rs b/crates/cowork-core/src/pipeline/executor/knowledge.rs index 96904a3..b7cdae2 100644 --- a/crates/cowork-core/src/pipeline/executor/knowledge.rs +++ b/crates/cowork-core/src/pipeline/executor/knowledge.rs @@ -133,7 +133,7 @@ pub async fn generate_iteration_knowledge( )?; println!("[Executor] Setting iteration ID for tool context..."); - crate::storage::set_iteration_id(iteration.id.clone()); + crate::persistence::set_iteration_id(iteration.id.clone()); let prompt = "Please analyze this iteration and generate a comprehensive knowledge snapshot. Use the available tools to load document summaries, examine the codebase structure, and extract meaningful knowledge."; diff --git a/crates/cowork-core/src/pipeline/executor/mod.rs b/crates/cowork-core/src/pipeline/executor/mod.rs index 54bee84..781cd50 100644 --- a/crates/cowork-core/src/pipeline/executor/mod.rs +++ b/crates/cowork-core/src/pipeline/executor/mod.rs @@ -160,7 +160,7 @@ impl IterationExecutor { let total_stages = stages.len(); let ctx = PipelineContext::new(project.clone(), iteration.clone(), workspace.clone()); - crate::storage::set_iteration_id(iteration.id.clone()); + crate::persistence::set_iteration_id(iteration.id.clone()); for (stage_idx, stage) in stages.into_iter().enumerate() { let stage_name = stage.name().to_string(); @@ -210,7 +210,7 @@ impl IterationExecutor { let mut current_feedback: Option = None; let mut feedback_loop_count: u32 = 0; - if let Ok(feedback_history) = crate::storage::load_feedback_history() { + if let Ok(feedback_history) = crate::persistence::load_feedback_history() { if let Some(fb) = feedback_history .feedbacks .iter() @@ -245,7 +245,7 @@ impl IterationExecutor { ) .await; - if let Err(e) = crate::storage::clear_stage_feedback(&stage_name) { + if let Err(e) = crate::persistence::clear_stage_feedback(&stage_name) { eprintln!("[Warning] Failed to clear feedback for stage '{}': {}", stage_name, e); } @@ -294,7 +294,7 @@ impl IterationExecutor { break; } - if let Err(e) = crate::storage::clear_stage_feedback(&stage_name) { + if let Err(e) = crate::persistence::clear_stage_feedback(&stage_name) { eprintln!("[Warning] Failed to clear feedback for stage '{}': {}", stage_name, e); } diff --git a/crates/cowork-core/src/pipeline/stage_executor.rs b/crates/cowork-core/src/pipeline/stage_executor.rs index 87d1eda..9b83700 100644 --- a/crates/cowork-core/src/pipeline/stage_executor.rs +++ b/crates/cowork-core/src/pipeline/stage_executor.rs @@ -13,7 +13,7 @@ use crate::interaction::{InteractiveBackend, MessageContext}; use crate::llm::{create_llm_client}; use crate::llm::config::load_config; use crate::pipeline::{PipelineContext, StageResult}; -use crate::storage::set_iteration_id; +use crate::persistence::set_iteration_id; use adk_core::{Content, Event}; use futures::StreamExt; use std::sync::Arc; @@ -55,7 +55,7 @@ pub async fn execute_stage_with_instruction( set_iteration_id(ctx.iteration.id.clone()); // Check for restart mode (GotoStage mechanism) - if let Ok(Some(session_meta)) = crate::storage::load_session_meta() { + if let Ok(Some(session_meta)) = crate::persistence::load_session_meta() { if let Some(restart_reason) = session_meta.restart_reason { // This is a restart from a previous stage interaction @@ -69,10 +69,10 @@ pub async fn execute_stage_with_instruction( .await; // Clear the restart reason after displaying it - if let Ok(mut meta) = crate::storage::load_session_meta() { + if let Ok(mut meta) = crate::persistence::load_session_meta() { if let Some(ref mut m) = meta { m.restart_reason = None; - let _ = crate::storage::save_session_meta(m); + let _ = crate::persistence::save_session_meta(m); } } } @@ -608,14 +608,17 @@ impl SimpleInvocationContext { branch: "main".to_string(), user_content: content.clone(), agent, - memory: None, // TODO: implement memory + // Memory and Artifacts are accessed via Tools (QueryMemoryTool, LoadArtifactTool, etc.) + // rather than through InvocationContext. This is intentional - tools provide more + // flexible access with proper validation and error handling. + memory: None, session: Box::new(SimpleSession::new(&ctx.iteration.id, content.clone())), run_config: adk_core::RunConfig { streaming_mode: adk_core::StreamingMode::SSE, ..adk_core::RunConfig::default() }, ended: std::sync::atomic::AtomicBool::new(false), - artifacts: None, // TODO: implement artifacts + artifacts: None, } } } diff --git a/crates/cowork-core/src/pipeline/stages/coding.rs b/crates/cowork-core/src/pipeline/stages/coding.rs index f4ce7b8..bfb98f6 100644 --- a/crates/cowork-core/src/pipeline/stages/coding.rs +++ b/crates/cowork-core/src/pipeline/stages/coding.rs @@ -35,7 +35,7 @@ impl CodingStage { feedback: Option<&str>, ) -> StageResult { // Set iteration ID for storage operations (must be set before any storage access) - crate::storage::set_iteration_id(ctx.iteration.id.clone()); + crate::persistence::set_iteration_id(ctx.iteration.id.clone()); let workspace = ctx.workspace_path.clone(); @@ -66,7 +66,7 @@ impl CodingStage { ) } else { // Try to load feedback from storage as fallback (for edge cases) - let stored_feedback = crate::storage::load_feedback_history() + let stored_feedback = crate::persistence::load_feedback_history() .ok() .and_then(|history| { history.feedbacks diff --git a/crates/cowork-core/src/tools/artifact_tools.rs b/crates/cowork-core/src/tools/artifact_tools.rs index deeed89..560e59b 100644 --- a/crates/cowork-core/src/tools/artifact_tools.rs +++ b/crates/cowork-core/src/tools/artifact_tools.rs @@ -1,5 +1,5 @@ // Artifact operation tools for Delivery Agent -use crate::storage::*; +use crate::persistence::*; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; use serde_json::{json, Value}; diff --git a/crates/cowork-core/src/tools/control_tools.rs b/crates/cowork-core/src/tools/control_tools.rs index 436a102..b00eeb8 100644 --- a/crates/cowork-core/src/tools/control_tools.rs +++ b/crates/cowork-core/src/tools/control_tools.rs @@ -1,6 +1,6 @@ // Control tools - provide_feedback, ask_user, etc. use crate::data::*; -use crate::storage::*; +use crate::persistence::*; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; use dialoguer::{Confirm, Input}; diff --git a/crates/cowork-core/src/tools/data_tools.rs b/crates/cowork-core/src/tools/data_tools.rs index cc0696b..3216726 100644 --- a/crates/cowork-core/src/tools/data_tools.rs +++ b/crates/cowork-core/src/tools/data_tools.rs @@ -1,6 +1,6 @@ // Data operation tools - Create and modify structured data use crate::data::*; -use crate::storage::*; +use crate::persistence::*; use adk_core::{Tool, ToolContext, AdkError}; use async_trait::async_trait; use serde_json::{json, Value}; diff --git a/crates/cowork-core/src/tools/deployment_tools.rs b/crates/cowork-core/src/tools/deployment_tools.rs index 0e40ddd..862797e 100644 --- a/crates/cowork-core/src/tools/deployment_tools.rs +++ b/crates/cowork-core/src/tools/deployment_tools.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::fs; use crate::persistence::IterationStore; -use crate::storage::get_iteration_id; +use crate::persistence::get_iteration_id; /// Helper function to strip UNC path prefix on Windows fn strip_unc_prefix(path: &std::path::Path) -> std::path::PathBuf { diff --git a/crates/cowork-core/src/tools/file_tools.rs b/crates/cowork-core/src/tools/file_tools.rs index 59f9d0b..0c36b50 100644 --- a/crates/cowork-core/src/tools/file_tools.rs +++ b/crates/cowork-core/src/tools/file_tools.rs @@ -10,7 +10,7 @@ use walkdir::WalkDir; use super::get_required_string_param; use crate::persistence::IterationStore; -use crate::storage::get_iteration_id; +use crate::persistence::get_iteration_id; // ============================================================================ // Helper Functions diff --git a/crates/cowork-core/src/tools/goto_stage_tool.rs b/crates/cowork-core/src/tools/goto_stage_tool.rs index 70c85d2..74ca725 100644 --- a/crates/cowork-core/src/tools/goto_stage_tool.rs +++ b/crates/cowork-core/src/tools/goto_stage_tool.rs @@ -1,6 +1,6 @@ // Goto Stage tool for Check Agent use crate::data::*; -use crate::storage::*; +use crate::persistence::*; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; use serde_json::{json, Value}; @@ -66,7 +66,7 @@ impl Tool for GotoStageTool { timestamp: chrono::Utc::now(), }; - if let Err(e) = crate::storage::append_feedback(&feedback) { + if let Err(e) = crate::persistence::append_feedback(&feedback) { // Log warning but don't fail the operation eprintln!("[GotoStageTool] Warning: Failed to save feedback: {}", e); } diff --git a/crates/cowork-core/src/tools/knowledge_tools.rs b/crates/cowork-core/src/tools/knowledge_tools.rs index a319636..6c290ad 100644 --- a/crates/cowork-core/src/tools/knowledge_tools.rs +++ b/crates/cowork-core/src/tools/knowledge_tools.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::fs; use crate::persistence::{IterationStore, MemoryStore}; -use crate::storage::get_iteration_id; +use crate::persistence::get_iteration_id; use crate::domain::IterationKnowledge; // ============================================================================ diff --git a/crates/cowork-core/src/tools/load_artifacts.rs b/crates/cowork-core/src/tools/load_artifacts.rs index 9c01a17..fdaf297 100644 --- a/crates/cowork-core/src/tools/load_artifacts.rs +++ b/crates/cowork-core/src/tools/load_artifacts.rs @@ -1,6 +1,6 @@ // Load artifact tools - Load specific artifact files with path restrictions -use crate::storage::artifact_path; +use crate::persistence::artifact_path; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; use serde_json::{json, Value}; diff --git a/crates/cowork-core/src/tools/pm_tools.rs b/crates/cowork-core/src/tools/pm_tools.rs index 4fa5db5..00baf6d 100644 --- a/crates/cowork-core/src/tools/pm_tools.rs +++ b/crates/cowork-core/src/tools/pm_tools.rs @@ -5,7 +5,7 @@ use crate::data::*; use crate::data::models::Stage; use crate::domain::{Iteration, memory::Decision}; use crate::persistence::{IterationStore, ProjectStore}; -use crate::storage::{append_feedback, save_session_meta, load_session_meta}; +use crate::persistence::{append_feedback, save_session_meta, load_session_meta}; use crate::data::models::SessionMeta; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; @@ -79,7 +79,7 @@ impl Tool for PMGotoStageTool { }; // Set iteration ID for storage operations BEFORE saving feedback - crate::storage::set_iteration_id(self.current_iteration_id.clone()); + crate::persistence::set_iteration_id(self.current_iteration_id.clone()); // Save feedback for the target stage (not "pm_agent") // This allows the target stage to find its feedback when loading from storage diff --git a/crates/cowork-core/src/tools/validation_tools.rs b/crates/cowork-core/src/tools/validation_tools.rs index 9a0b9b9..a432d0f 100644 --- a/crates/cowork-core/src/tools/validation_tools.rs +++ b/crates/cowork-core/src/tools/validation_tools.rs @@ -1,5 +1,5 @@ // Validation tools for checking data quality -use crate::storage::*; +use crate::persistence::*; use adk_core::{Tool, ToolContext}; use async_trait::async_trait; use serde_json::{json, Value}; diff --git a/crates/cowork-gui/src-tauri/src/commands/pm.rs b/crates/cowork-gui/src-tauri/src/commands/pm.rs index 88fcc25..d0485a9 100644 --- a/crates/cowork-gui/src-tauri/src/commands/pm.rs +++ b/crates/cowork-gui/src-tauri/src/commands/pm.rs @@ -214,11 +214,11 @@ pub async fn pm_restart_iteration( // This allows the coding stage to read it via load_feedback_history if let Some(ref fb) = feedback { // Set iteration ID for storage operations - cowork_core::storage::set_iteration_id(iteration_id.clone()); + cowork_core::persistence::set_iteration_id(iteration_id.clone()); println!("[PM] Set iteration_id for storage: {}", iteration_id); // Debug: print the storage path - if let Ok(iter_dir) = cowork_core::storage::get_iteration_dir() { + if let Ok(iter_dir) = cowork_core::persistence::get_iteration_dir() { println!("[PM] Storage iteration dir: {}", iter_dir.display()); } @@ -231,7 +231,7 @@ pub async fn pm_restart_iteration( timestamp: chrono::Utc::now(), }; - if let Err(e) = cowork_core::storage::append_feedback(&feedback_entry) { + if let Err(e) = cowork_core::persistence::append_feedback(&feedback_entry) { eprintln!("[PM] Warning: Failed to save feedback: {}", e); } else { println!("[PM] Saved feedback to storage ({} chars): {}", fb.len(), fb.chars().take(50).collect::()); diff --git a/crates/cowork-gui/src-tauri/src/config_commands.rs b/crates/cowork-gui/src-tauri/src/config_commands.rs index fad989b..65ecd06 100644 --- a/crates/cowork-gui/src-tauri/src/config_commands.rs +++ b/crates/cowork-gui/src-tauri/src/config_commands.rs @@ -41,7 +41,7 @@ impl From<&adk_skill::ParsedSkill> for SkillInfo { } } -/// V3 Config Registry state for frontend +/// Config Registry state for frontend #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConfigRegistryState { pub agents: HashMap, @@ -56,7 +56,7 @@ pub struct ConfigRegistryState { pub async fn gui_get_config_registry() -> Result { // Get the global registry let registry = cowork_core::config_definition::global_registry(); - + // Extract all configurations let mut agents = HashMap::new(); for id in registry.list_agents() { @@ -64,21 +64,21 @@ pub async fn gui_get_config_registry() -> Result { agents.insert(id, agent); } } - + let mut stages = HashMap::new(); for id in registry.list_stages() { if let Some(stage) = registry.get_stage(&id) { stages.insert(id, stage); } } - + let mut flows = HashMap::new(); for id in registry.list_flows() { if let Some(flow) = registry.get_flow(&id) { flows.insert(id, flow); } } - + // Get skills from SkillManager (using current directory as project root) let skills = match SkillManager::for_project(".") { Ok(manager) => manager.list_skills().iter().map(SkillInfo::from).collect(), @@ -87,17 +87,17 @@ pub async fn gui_get_config_registry() -> Result { Vec::new() } }; - + let mut integrations = HashMap::new(); for id in registry.list_integrations() { if let Some(integration) = registry.get_integration(&id) { integrations.insert(id, integration); } } - + // Get default flow ID let default_flow_id = registry.get_default_flow_id(); - + Ok(ConfigRegistryState { agents, stages, @@ -112,20 +112,20 @@ pub async fn gui_get_config_registry() -> Result { #[tauri::command] pub async fn gui_reset_config_registry() -> Result { let registry = cowork_core::config_definition::global_registry(); - + // Clear existing configurations registry.clear() .map_err(|e| format!("Failed to clear registry: {}", e))?; - + // Reload built-in configurations let report = cowork_core::config_definition::load_builtin_configs(®istry) .map_err(|e| format!("Failed to load built-in configs: {}", e))?; - + tracing::info!( "Reset config registry: {} agents, {} stages, {} flows", report.agents_loaded, report.stages_loaded, report.flows_loaded ); - + // Return the reset state gui_get_config_registry().await } @@ -227,36 +227,36 @@ pub async fn gui_get_builtin_instructions() -> Result, S content: PROJECT_MANAGER_AGENT_INSTRUCTION.to_string(), }, ]; - + Ok(instructions) } #[tauri::command] pub async fn gui_save_agent_config(agent: AgentDefinition) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Register in memory registry.register_agent(agent.clone()) .map_err(|e| format!("Failed to save agent: {}", e))?; - + // Persist to file registry.save_agent_to_file(&agent) .map_err(|e| format!("Failed to persist agent to file: {}", e))?; - + Ok(()) } #[tauri::command] pub async fn gui_delete_agent_config(agent_id: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Remove from memory let removed = registry.unregister_agent(&agent_id); - + // Delete from file (regardless of memory removal result) registry.delete_agent_file(&agent_id) .map_err(|e| format!("Failed to delete agent file: {}", e))?; - + if removed { Ok(()) } else { @@ -269,96 +269,96 @@ pub async fn gui_delete_agent_config(agent_id: String) -> Result<(), String> { #[tauri::command] pub async fn gui_save_stage_config(stage: StageDefinition) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Register in memory registry.register_stage(stage.clone()) .map_err(|e| format!("Failed to save stage: {}", e))?; - + // Persist to file registry.save_stage_to_file(&stage) .map_err(|e| format!("Failed to persist stage to file: {}", e))?; - + Ok(()) } #[tauri::command] pub async fn gui_delete_stage_config(stage_id: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Delete from file registry.delete_stage_file(&stage_id) .map_err(|e| format!("Failed to delete stage file: {}", e))?; - + Ok(()) } #[tauri::command] pub async fn gui_save_flow_config(flow: FlowDefinition) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Check if trying to modify a built-in flow if let Some(existing) = registry.get_flow(&flow.id) { if existing.is_builtin { return Err("Cannot modify built-in flow. Create a new flow instead.".to_string()); } } - + // Ensure user flows are not marked as builtin let mut flow = flow; flow.is_builtin = false; - + // Register in memory registry.register_flow(flow.clone()) .map_err(|e| format!("Failed to save flow: {}", e))?; - + // Persist to file registry.save_flow_to_file(&flow) .map_err(|e| format!("Failed to persist flow to file: {}", e))?; - + Ok(()) } #[tauri::command] pub async fn gui_delete_flow_config(flow_id: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Check if trying to delete a built-in flow if let Some(flow) = registry.get_flow(&flow_id) { if flow.is_builtin { return Err("Cannot delete built-in flow.".to_string()); } } - + // Remove from memory registry.unregister_flow(&flow_id); - + // Delete from file registry.delete_flow_file(&flow_id) .map_err(|e| format!("Failed to delete flow file: {}", e))?; - + // If this was the default flow, clear the default setting if registry.get_default_flow_id().as_deref() == Some(&flow_id) { registry.set_default_flow(None) .map_err(|e| format!("Failed to clear default flow: {}", e))?; } - + Ok(()) } #[tauri::command] pub async fn gui_set_default_flow(flow_id: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Verify the flow exists if registry.get_flow(&flow_id).is_none() { return Err(format!("Flow not found: {}", flow_id)); } - + // Set as default let flow_id_clone = flow_id.clone(); registry.set_default_flow(Some(flow_id)) .map_err(|e| format!("Failed to set default flow: {}", e))?; - + tracing::info!("Set default flow to: {}", flow_id_clone); Ok(()) } @@ -367,36 +367,36 @@ pub async fn gui_set_default_flow(flow_id: String) -> Result<(), String> { #[tauri::command] pub async fn gui_install_skill(skill_path: String) -> Result { use std::path::Path; - + // Strip @ prefix if present (Tauri dialog may add this) let skill_path = skill_path.strip_prefix('@').unwrap_or(&skill_path); let path = Path::new(skill_path); - + // Read and parse SKILL.md first to get skill info let skill_md_path = path.join("SKILL.md"); if !skill_md_path.exists() { return Err(format!("Source directory does not contain SKILL.md: {:?}", path)); } - + let content = std::fs::read_to_string(&skill_md_path) .map_err(|e| format!("Failed to read SKILL.md: {}", e))?; - + let parsed = adk_skill::parse_skill_markdown(&skill_md_path, &content) .map_err(|e| format!("Failed to parse SKILL.md: {}", e))?; - + let skill_name = parsed.name.clone(); - + // Create target directory and copy skill files let target_dir = Path::new(".").join(".skills").join(&skill_name); std::fs::create_dir_all(&target_dir) .map_err(|e| format!("Failed to create target directory: {}", e))?; - + // Copy all files from source to target copy_dir_all(path, &target_dir) .map_err(|e| format!("Failed to copy skill files: {}", e))?; - + tracing::info!("Installed skill '{}' to {:?}", skill_name, target_dir); - + // Return SkillInfo from the parsed skill document Ok(SkillInfo::from(&parsed)) } @@ -405,14 +405,14 @@ pub async fn gui_install_skill(skill_path: String) -> Result fn copy_dir_all(src: &std::path::Path, dst: &std::path::Path) -> Result<(), String> { std::fs::create_dir_all(dst) .map_err(|e| format!("Failed to create directory: {}", e))?; - + for entry in std::fs::read_dir(src) - .map_err(|e| format!("Failed to read directory: {}", e))? + .map_err(|e| format!("Failed to read directory: {}", e))? { let entry = entry.map_err(|e| format!("Failed to read entry: {}", e))?; let ty = entry.file_type() .map_err(|e| format!("Failed to get file type: {}", e))?; - + if ty.is_dir() { copy_dir_all(&entry.path(), &dst.join(entry.file_name()))?; } else { @@ -420,7 +420,7 @@ fn copy_dir_all(src: &std::path::Path, dst: &std::path::Path) -> Result<(), Stri .map_err(|e| format!("Failed to copy file: {}", e))?; } } - + Ok(()) } @@ -428,10 +428,10 @@ fn copy_dir_all(src: &std::path::Path, dst: &std::path::Path) -> Result<(), Stri #[tauri::command] pub async fn gui_uninstall_skill(skill_name: String) -> Result<(), String> { use std::path::Path; - + // Get skills directory let skills_dir = Path::new(".").join(".skills").join(&skill_name); - + if skills_dir.exists() { std::fs::remove_dir_all(&skills_dir) .map_err(|e| format!("Failed to remove skill directory: {}", e))?; @@ -439,33 +439,33 @@ pub async fn gui_uninstall_skill(skill_name: String) -> Result<(), String> { } else { tracing::warn!("Skill directory not found: {:?}", skills_dir); } - + Ok(()) } #[tauri::command] pub async fn gui_save_integration_config(integration: IntegrationDefinition) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Register in memory registry.register_integration(integration.clone()) .map_err(|e| format!("Failed to save integration: {}", e))?; - + // Persist to file registry.save_integration_to_file(&integration) .map_err(|e| format!("Failed to persist integration to file: {}", e))?; - + Ok(()) } #[tauri::command] pub async fn gui_delete_integration_config(integration_id: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + // Delete from file registry.delete_integration_file(&integration_id) .map_err(|e| format!("Failed to delete integration file: {}", e))?; - + Ok(()) } @@ -499,7 +499,7 @@ impl From for Valid severity: "warning".to_string(), })) .collect(); - + Self { valid: result.is_valid, issues, @@ -526,7 +526,7 @@ pub async fn gui_validate_flow_config(flow: FlowDefinition) -> Result Result { let registry = cowork_core::config_definition::global_registry(); - + let json = match config_type.as_str() { "agent" => { let agent = registry.get_agent(&config_id) @@ -548,14 +548,14 @@ pub async fn gui_export_config(config_type: String, config_id: String) -> Result } _ => return Err(format!("Unknown config type: {}", config_type)), }; - + Ok(json) } #[tauri::command] pub async fn gui_import_config(config_type: String, json_data: String) -> Result<(), String> { let registry = cowork_core::config_definition::global_registry(); - + match config_type.as_str() { "agent" => { let agent: AgentDefinition = serde_json::from_str(&json_data) @@ -577,7 +577,7 @@ pub async fn gui_import_config(config_type: String, json_data: String) -> Result } _ => return Err(format!("Unknown config type: {}", config_type)), }; - + Ok(()) } @@ -612,10 +612,10 @@ pub async fn get_default_config() -> ModelConfig { pub async fn open_config_folder() -> Result<(), String> { let config_path = config::get_config_path() .map_err(|e| format!("Failed to get config path: {}", e))?; - + let parent = config_path.parent() .ok_or_else(|| "Failed to get config directory".to_string())?; - + open_folder_in_explorer(parent) } @@ -631,22 +631,22 @@ pub async fn test_llm_connection(llm_config: LlmConfig) -> Result let client = create_llm_client(&llm_config) .map_err(|e| format!("Failed to create LLM client: {}", e))?; - + let contents = vec![Content { role: "user".to_string(), parts: vec![Part::Text { text: "Hello, this is a connection test. Please respond with 'OK'.".to_string() }], }]; - + let test_request = LlmRequest { model: llm_config.model_name.clone(), contents, config: None, tools: Default::default(), }; - + let mut stream = client.generate_content(test_request, false).await .map_err(|e| format!("Connection test failed: {}", e))?; - + let mut response_text = String::new(); while let Some(chunk) = stream.next().await { if let Ok(r) = chunk { @@ -659,11 +659,11 @@ pub async fn test_llm_connection(llm_config: LlmConfig) -> Result } } } - + if response_text.is_empty() { return Err("Connection test failed: Empty response from API".to_string()); } - + Ok(true) } @@ -680,7 +680,7 @@ fn open_folder_in_explorer(path: &std::path::Path) -> Result<(), String> { .spawn() .map_err(|e| format!("Failed to open folder: {}", e))?; } - + #[cfg(target_os = "macos")] { std::process::Command::new("open") @@ -688,7 +688,7 @@ fn open_folder_in_explorer(path: &std::path::Path) -> Result<(), String> { .spawn() .map_err(|e| format!("Failed to open folder: {}", e))?; } - + #[cfg(target_os = "linux")] { std::process::Command::new("xdg-open") @@ -696,6 +696,6 @@ fn open_folder_in_explorer(path: &std::path::Path) -> Result<(), String> { .spawn() .map_err(|e| format!("Failed to open folder: {}", e))?; } - + Ok(()) -} \ No newline at end of file +} diff --git a/crates/cowork-gui/src-tauri/src/lib.rs b/crates/cowork-gui/src-tauri/src/lib.rs index e6eb520..9a0cbd3 100644 --- a/crates/cowork-gui/src-tauri/src/lib.rs +++ b/crates/cowork-gui/src-tauri/src/lib.rs @@ -659,7 +659,7 @@ pub fn run() { // Get config_ready flag for async initialization let config_ready = app.state::().config_ready.clone(); - // Initialize V3 config registry asynchronously to avoid blocking startup + // Initialize config registry asynchronously to avoid blocking startup // Use tauri::async_runtime::spawn instead of tokio::spawn for Tauri 2.x compatibility tauri::async_runtime::spawn(async move { println!("[GUI] Starting async config registry initialization..."); @@ -816,7 +816,7 @@ pub fn run() { config_commands::open_config_folder, config_commands::test_llm_connection, config_commands::has_valid_config, - // V3 Config commands + // Config commands config_commands::gui_get_config_registry, config_commands::gui_reset_config_registry, config_commands::gui_save_agent_config, diff --git a/crates/cowork-gui/src/types/index.ts b/crates/cowork-gui/src/types/index.ts index 925681c..5d4037d 100644 --- a/crates/cowork-gui/src/types/index.ts +++ b/crates/cowork-gui/src/types/index.ts @@ -44,7 +44,7 @@ export type { KnowledgeListResult, } from './knowledge'; -// V3 Config types +// Config types export type { AgentType, ModelConfig, diff --git a/litho.docs/en/1.Overview.md b/litho.docs/en/1.Overview.md index 933461e..52f33d1 100644 --- a/litho.docs/en/1.Overview.md +++ b/litho.docs/en/1.Overview.md @@ -114,7 +114,7 @@ The system boundary encompasses the following core architectural components: | **Core Domain Logic** | Project and Iteration aggregates, lifecycle management, inheritance mode logic | | **AI Pipeline Engine** | 7-stage workflow controller, Stage Executor with ADK integration, Actor-Critic pattern implementation, modular Iteration Executor | | **Agent Instruction System** | ~2000 lines of prompt engineering (Actor/Critic instructions per stage, knowledge generation, Legacy Project Analyzer) | -| **Configuration System (V3)** | Config Registry, Agent/Stage/Flow/Skill/Integration definitions, user config management, validation | +| **Configuration System** | Config Registry, Agent/Stage/Flow/Skill/Integration definitions, user config management, validation | | **Skills Module** | adk-skill integration (agentskills.io standard), skill discovery, selection, and injection | | **Tool Ecosystem** | 30+ ADK Tools (File Tools, Data Tools, Validation Tools, HITL Tools, Memory Tools, Deployment Tools, Legacy Project Analysis Tools) | | **Memory Management** | ProjectMemory and IterationKnowledge domains, query indexing, knowledge promotion workflows | @@ -232,8 +232,8 @@ All file operations are constrained to designated workspace directories with pat ### 6.5 Event-Driven GUI Architecture The Tauri-based GUI employs an event-driven model for real-time execution monitoring. Backend events (agent messages, tool calls, streaming responses, progress updates) are emitted to the React frontend, enabling live visualization of AI agent activities without polling overhead. -### 6.6 V3 Data-Driven Configuration System -The V3 architecture introduces a configuration-driven approach where Agents, Stages, Flows, Skills, and Integrations are defined as JSON configurations rather than hardcoded implementations. This enables users to customize development workflows, create specialized agents, and extend system capabilities without modifying source code. The configuration registry manages built-in and user-defined configurations with validation and hot-reload support. +### 6.6 Data-Driven Configuration System +Introduces a configuration-driven approach where Agents, Stages, Flows, Skills, and Integrations are defined as JSON configurations rather than hardcoded implementations. This enables users to customize development workflows, create specialized agents, and extend system capabilities without modifying source code. The configuration registry manages built-in and user-defined configurations with validation and hot-reload support. ### 6.7 Legacy Project Import and Reverse Engineering @@ -282,4 +282,4 @@ The skills module implements the agentskills.io standard for domain-specific cap **Document Version**: 1.0 **Classification**: Architecture Documentation (C4 System Context Level) -**Next Steps**: Refer to Container and Component Level diagrams for detailed internal architecture documentation. \ No newline at end of file +**Next Steps**: Refer to Container and Component Level diagrams for detailed internal architecture documentation. diff --git a/litho.docs/en/4.Deep-Exploration/Persistence Domain.md b/litho.docs/en/4.Deep-Exploration/Persistence Domain.md index 794ffc1..78c4f24 100644 --- a/litho.docs/en/4.Deep-Exploration/Persistence Domain.md +++ b/litho.docs/en/4.Deep-Exploration/Persistence Domain.md @@ -153,17 +153,59 @@ Manages iteration entities and their associated workspace artifacts. Implements │ └── knowledge.json ``` -### 3.3 Storage Utilities +### 3.3 Iteration Data Storage -**Location:** `crates/cowork-core/src/storage/*.rs` +**Location:** `crates/cowork-core/src/persistence/iteration_data.rs` -Provides low-level storage primitives and cross-cutting concerns for persistence operations. +Provides iteration-scoped data storage for artifacts, session data, and stage-specific content. This module was consolidated from the former `storage` module, centralizing all persistence operations within the persistence domain. **Key Functions:** -- **`get_cowork_dir()`**: Resolves the `.cowork-v2` directory path relative to project root with platform-specific path normalization (handles UNC paths on Windows) -- **`get_iteration_id()`**: Retrieves current active iteration identifier from global state or filesystem -- **Path Validation**: Ensures all file operations remain within project workspace boundaries (security containment) +| Category | Functions | Description | +|----------|-----------|-------------| +| **Iteration Context** | `set_iteration_id()`, `get_iteration_id()`, `clear_iteration_id()` | Manage global iteration context via thread-safe static storage | +| **Directory Management** | `get_iteration_dir()`, `artifact_path()`, `data_path()`, `session_path()` | Resolve iteration-specific paths for data, artifacts, and session files | +| **Stage Data** | `load/save_requirements()`, `load/save_feature_list()`, `load/save_design_spec()`, `load/save_implementation_plan()` | Persist structured data models for each pipeline stage | +| **Artifacts** | `load/save_idea()`, `save_plan_doc()`, `save_prd_doc()`, `save_design_doc()`, `save_check_report()`, `save_delivery_report()` | Manage markdown artifact files | +| **Session State** | `load/save_session_meta()`, `load/save_feedback_history()`, `append_feedback()`, `clear_stage_feedback()` | Track execution state and user feedback | +| **Workspace Utils** | `get_cowork_dir()`, `is_project_initialized()`, `init_project_structure()` | Global workspace path resolution and initialization | + +**Iteration Directory Structure:** +``` +.cowork-v2/ +├── project.json +├── iterations/ +│ └── {iteration_id}/ +│ ├── data/ # Structured JSON data +│ │ ├── requirements.json +│ │ ├── feature_list.json +│ │ ├── design_spec.json +│ │ ├── implementation_plan.json +│ │ └── code_metadata.json +│ ├── artifacts/ # Markdown documents +│ │ ├── idea.md +│ │ ├── prd.md +│ │ ├── design.md +│ │ ├── plan.md +│ │ ├── check_report.md +│ │ └── delivery_report.md +│ ├── session/ # Execution state +│ │ ├── meta.json +│ │ └── feedback.json +│ └── logs/ # Execution logs +└── memory/ + ├── project/ + └── iterations/ +``` + +**Global State Management:** + +The module uses global static storage for iteration context, enabling tools and pipeline stages to access current iteration data without explicit parameter passing: + +```rust +static CURRENT_ITERATION_ID: Mutex> = Mutex::new(None); +static GLOBAL_WORKSPACE_PATH: OnceLock>> = OnceLock::new(); +``` --- @@ -332,7 +374,7 @@ flowchart LR The V2 architecture (current) maintains backward compatibility considerations: - Schema evolution handled through serde's `default` attributes for new fields -- Directory structure versioned via `.cowork-v2` naming convention (allows future V3 alongside) +- Directory structure versioned via `.cowork-v2` naming convention - No automated migration tools currently implemented; manual migration scripts for major version upgrades ### 8.3 Configuration diff --git "a/litho.docs/zh/1\343\200\201\351\241\271\347\233\256\346\246\202\350\277\260.md" "b/litho.docs/zh/1\343\200\201\351\241\271\347\233\256\346\246\202\350\277\260.md" index 4e52443..84c834b 100644 --- "a/litho.docs/zh/1\343\200\201\351\241\271\347\233\256\346\246\202\350\277\260.md" +++ "b/litho.docs/zh/1\343\200\201\351\241\271\347\233\256\346\246\202\350\277\260.md" @@ -227,8 +227,8 @@ C4Context ### 6.5 事件驱动 GUI 架构 基于 Tauri 的 GUI 采用事件驱动模型进行实时执行监控。后端事件(智能体消息、工具调用、流式响应、进度更新)被发射到 React 前端,实现 AI 智能体活动的实时可视化,无需轮询开销。 -### 6.6 V3 数据驱动配置系统 -V3 架构引入了配置驱动的方法,Agent、Stage、Flow、Skill 和 Integration 定义为 JSON 配置而非硬编码实现。这使用户能够自定义开发工作流、创建专用智能体并扩展系统能力,而无需修改源代码。配置注册表管理内置和用户定义的配置,支持验证和热重载。 +### 6.6 数据驱动配置系统 +引入了配置驱动的方法,Agent、Stage、Flow、Skill 和 Integration 定义为 JSON 配置而非硬编码实现。这使用户能够自定义开发工作流、创建专用智能体并扩展系统能力,而无需修改源代码。配置注册表管理内置和用户定义的配置,支持验证和热重载。 ### 6.7 现有项目导入与逆向分析 diff --git "a/litho.docs/zh/2\343\200\201\346\236\266\346\236\204\346\246\202\350\247\210.md" "b/litho.docs/zh/2\343\200\201\346\236\266\346\236\204\346\246\202\350\247\210.md" index 7223837..94526cf 100644 --- "a/litho.docs/zh/2\343\200\201\346\236\266\346\236\204\346\246\202\350\247\210.md" +++ "b/litho.docs/zh/2\343\200\201\346\236\266\346\236\204\346\246\202\350\247\210.md" @@ -918,7 +918,7 @@ flowchart TB ## 配置系统架构 -Cowork Forge V3 引入了数据驱动的配置系统,将原本硬编码的 Agent、Stage、Flow、Skill 和 Integration 定义转为可配置的 JSON 格式。这使系统更加灵活、可扩展,用户可以自定义开发流程而无需修改代码。 +Cowork Forge 引入了数据驱动的配置系统,将原本硬编码的 Agent、Stage、Flow、Skill 和 Integration 定义转为可配置的 JSON 格式。这使系统更加灵活、可扩展,用户可以自定义开发流程而无需修改代码。 ### 配置注册表 diff --git "a/litho.docs/zh/4\343\200\201\346\267\261\345\205\245\346\216\242\347\264\242/4.5\343\200\201Artifacts\345\255\230\345\202\250.md" "b/litho.docs/zh/4\343\200\201\346\267\261\345\205\245\346\216\242\347\264\242/4.5\343\200\201Artifacts\345\255\230\345\202\250.md" index e8caaeb..756e2de 100644 --- "a/litho.docs/zh/4\343\200\201\346\267\261\345\205\245\346\216\242\347\264\242/4.5\343\200\201Artifacts\345\255\230\345\202\250.md" +++ "b/litho.docs/zh/4\343\200\201\346\267\261\345\205\245\346\216\242\347\264\242/4.5\343\200\201Artifacts\345\255\230\345\202\250.md" @@ -153,17 +153,59 @@ flowchart TD │ └── knowledge.json ``` -### 3.3 存储工具 +### 3.3 迭代数据存储 -**位置:** `crates/cowork-core/src/storage/*.rs` +**位置:** `crates/cowork-core/src/persistence/iteration_data.rs` -为持久化操作提供低级存储原语和横切关注点。 +提供迭代作用域的数据存储,用于工件、会话数据和阶段特定内容。该模块从原 `storage` 模块整合而来,将所有持久化操作集中到持久化域内。 **关键功能:** -- **`get_cowork_dir()`**:解析与项目根相对的 `.cowork-v2` 目录路径,带平台特定路径规范化(处理 Windows UNC 路径) -- **`get_iteration_id()`**:从全局状态或文件系统检索当前活动迭代标识符 -- **路径验证**:确保所有文件操作保持在项目工作区边界内(安全包含) +| 类别 | 函数 | 描述 | +|----------|-----------|-------------| +| **迭代上下文** | `set_iteration_id()`, `get_iteration_id()`, `clear_iteration_id()` | 通过线程安全静态存储管理全局迭代上下文 | +| **目录管理** | `get_iteration_dir()`, `artifact_path()`, `data_path()`, `session_path()` | 解析迭代特定的数据、工件和会话文件路径 | +| **阶段数据** | `load/save_requirements()`, `load/save_feature_list()`, `load/save_design_spec()`, `load/save_implementation_plan()` | 持久化每个流程阶段的结构化数据模型 | +| **工件** | `load/save_idea()`, `save_plan_doc()`, `save_prd_doc()`, `save_design_doc()`, `save_check_report()`, `save_delivery_report()` | 管理 markdown 工件文件 | +| **会话状态** | `load/save_session_meta()`, `load/save_feedback_history()`, `append_feedback()`, `clear_stage_feedback()` | 跟踪执行状态和用户反馈 | +| **工作区工具** | `get_cowork_dir()`, `is_project_initialized()`, `init_project_structure()` | 全局工作区路径解析和初始化 | + +**迭代目录结构:** +``` +.cowork-v2/ +├── project.json +├── iterations/ +│ └── {iteration_id}/ +│ ├── data/ # 结构化 JSON 数据 +│ │ ├── requirements.json +│ │ ├── feature_list.json +│ │ ├── design_spec.json +│ │ ├── implementation_plan.json +│ │ └── code_metadata.json +│ ├── artifacts/ # Markdown 文档 +│ │ ├── idea.md +│ │ ├── prd.md +│ │ ├── design.md +│ │ ├── plan.md +│ │ ├── check_report.md +│ │ └── delivery_report.md +│ ├── session/ # 执行状态 +│ │ ├── meta.json +│ │ └── feedback.json +│ └── logs/ # 执行日志 +└── memory/ + ├── project/ + └── iterations/ +``` + +**全局状态管理:** + +该模块使用全局静态存储管理迭代上下文,使工具和流程阶段无需显式参数传递即可访问当前迭代数据: + +```rust +static CURRENT_ITERATION_ID: Mutex> = Mutex::new(None); +static GLOBAL_WORKSPACE_PATH: OnceLock>> = OnceLock::new(); +``` --- @@ -332,7 +374,7 @@ flowchart LR V2 架构(当前)保持向后兼容性考虑: - 模式演化通过 serde 的 `default` 属性处理新字段 -- 目录结构通过 `.cowork-v2` 命名约定版本化(允许未来 V3 并行安装) +- 目录结构通过 `.cowork-v2` 命名约定版本化 - 当前未实现自动化迁移工具;重大版本升级的手动迁移脚本 ### 8.3 配置