Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CreedFlow/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PackageDescription

let package = Package(
name: "CreedFlow",
defaultLocalization: "en",
platforms: [
.macOS(.v14)
],
Expand All @@ -25,6 +26,8 @@ let package = Package(
path: "Sources/CreedFlow",
resources: [
.copy("Resources/AppIcon-preview.png"),
.process("Resources/en.lproj"),
.process("Resources/tr.lproj"),
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
Expand Down
20 changes: 20 additions & 0 deletions CreedFlow/Sources/CreedFlow/Database/AppDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,26 @@ public struct AppDatabase {
)
}

migrator.registerMigration("v21_project_completion_and_comments") { db in
try db.alter(table: "project") { t in
t.add(column: "completedAt", .datetime)
}

try db.create(table: "taskComment") { t in
t.primaryKey("id", .text).notNull()
t.column("taskId", .text).notNull()
.references("agentTask", onDelete: .cascade)
t.column("content", .text).notNull()
t.column("author", .text).notNull().defaults(to: "user")
t.column("createdAt", .datetime).notNull()
}
try db.create(
index: "taskComment_on_taskId",
on: "taskComment",
columns: ["taskId"]
)
}

return migrator
}
}
Expand Down
20 changes: 18 additions & 2 deletions CreedFlow/Sources/CreedFlow/Engine/Orchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1590,17 +1590,33 @@ final class Orchestrator {
}
}

/// Check if all tasks for a project are done and update project + prompt usage accordingly.
/// Check if all tasks for a project are done and update project status + prompt usage accordingly.
private func checkProjectCompletion(projectId: UUID) async {
do {
let (allDone, anyFailed) = try await dbQueue.read { db -> (Bool, Bool) in
let tasks = try AgentTask.filter(Column("projectId") == projectId).fetchAll(db)
let tasks = try AgentTask
.filter(Column("projectId") == projectId)
.filter(Column("archivedAt") == nil)
.fetchAll(db)
let pending = tasks.contains { $0.status == .queued || $0.status == .inProgress }
let failed = tasks.contains { $0.status == .failed }
return (!pending, failed)
}
guard allDone else { return }

// Set project completedAt if not already set
try await dbQueue.write { db in
guard var project = try Project.fetchOne(db, id: projectId) else { return }
if project.completedAt == nil {
project.completedAt = Date()
if !anyFailed {
project.status = .completed
}
project.updatedAt = Date()
try project.update(db)
}
}

let outcome: PromptUsage.Outcome = anyFailed ? .failed : .completed
await backfillPromptUsageOutcome(projectId: projectId, outcome: outcome)
} catch {
Expand Down
3 changes: 3 additions & 0 deletions CreedFlow/Sources/CreedFlow/Models/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package struct Project: Codable, Identifiable, Equatable {
package var directoryPath: String
package var projectType: ProjectType
package var stagingPrNumber: Int?
package var completedAt: Date?
package var telegramChatId: Int64?
package var createdAt: Date
package var updatedAt: Date
Expand Down Expand Up @@ -43,6 +44,7 @@ package struct Project: Codable, Identifiable, Equatable {
directoryPath: String = "",
projectType: ProjectType = .software,
stagingPrNumber: Int? = nil,
completedAt: Date? = nil,
telegramChatId: Int64? = nil,
createdAt: Date = Date(),
updatedAt: Date = Date()
Expand All @@ -55,6 +57,7 @@ package struct Project: Codable, Identifiable, Equatable {
self.directoryPath = directoryPath
self.projectType = projectType
self.stagingPrNumber = stagingPrNumber
self.completedAt = completedAt
self.telegramChatId = telegramChatId
self.createdAt = createdAt
self.updatedAt = updatedAt
Expand Down
172 changes: 172 additions & 0 deletions CreedFlow/Sources/CreedFlow/Models/ProjectTemplate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import Foundation

package struct ProjectTemplate: Identifiable {
package let id: String
package let name: String
package let description: String
package let icon: String
package let techStack: String
package let projectType: Project.ProjectType
package let features: [TemplateFeature]
}

package struct TemplateFeature {
package let name: String
package let description: String
package let tasks: [TemplateTask]
}

package struct TemplateTask {
package let agentType: AgentTask.AgentType
package let title: String
package let description: String
package let priority: Int
}

// MARK: - Built-in Templates

extension ProjectTemplate {
package static let builtInTemplates: [ProjectTemplate] = [
webApp, mobileApp, restAPI, landingPage, blogCMS, cliTool,
]

static let webApp = ProjectTemplate(
id: "web-app",
name: "Web App",
description: "Full-stack web application with authentication, CRUD operations, and deployment",
icon: "globe",
techStack: "React, Node.js, PostgreSQL",
projectType: .software,
features: [
TemplateFeature(name: "Authentication", description: "User registration, login, and session management", tasks: [
TemplateTask(agentType: .coder, title: "Implement auth API endpoints", description: "Create signup, login, logout, and password reset endpoints with JWT tokens", priority: 9),
TemplateTask(agentType: .coder, title: "Build auth UI components", description: "Create login, register, and forgot password forms with validation", priority: 8),
TemplateTask(agentType: .tester, title: "Test authentication flow", description: "Write integration tests for auth endpoints and UI", priority: 7),
]),
TemplateFeature(name: "CRUD Operations", description: "Core data management with list, create, edit, and delete", tasks: [
TemplateTask(agentType: .coder, title: "Implement data API endpoints", description: "Create REST endpoints for CRUD operations with pagination and filtering", priority: 8),
TemplateTask(agentType: .coder, title: "Build data management UI", description: "Create list view, detail view, and forms for data management", priority: 7),
TemplateTask(agentType: .tester, title: "Test CRUD operations", description: "Write tests for all CRUD endpoints and UI interactions", priority: 6),
]),
TemplateFeature(name: "Deployment", description: "Docker-based deployment configuration", tasks: [
TemplateTask(agentType: .devops, title: "Set up Docker configuration", description: "Create Dockerfile and docker-compose.yml for the application", priority: 5),
TemplateTask(agentType: .devops, title: "Configure CI/CD pipeline", description: "Set up automated build, test, and deploy pipeline", priority: 4),
]),
]
)

static let mobileApp = ProjectTemplate(
id: "mobile-app",
name: "Mobile App",
description: "Cross-platform mobile application with native UI, API integration, and push notifications",
icon: "iphone",
techStack: "React Native, TypeScript",
projectType: .software,
features: [
TemplateFeature(name: "App UI", description: "Core screens and navigation", tasks: [
TemplateTask(agentType: .designer, title: "Design app screens", description: "Create design specs for main screens: home, detail, profile, settings", priority: 9),
TemplateTask(agentType: .coder, title: "Implement navigation and screens", description: "Build tab navigation, stack navigation, and core screen layouts", priority: 8),
]),
TemplateFeature(name: "API Integration", description: "Backend API connectivity", tasks: [
TemplateTask(agentType: .coder, title: "Set up API client", description: "Configure HTTP client, authentication headers, and error handling", priority: 8),
TemplateTask(agentType: .coder, title: "Implement data fetching", description: "Add API calls for all screens with loading states and caching", priority: 7),
]),
TemplateFeature(name: "Authentication", description: "User auth with secure storage", tasks: [
TemplateTask(agentType: .coder, title: "Implement auth flow", description: "Build login, register, and token refresh with secure storage", priority: 8),
TemplateTask(agentType: .tester, title: "Test auth and API integration", description: "Write tests for auth flow and API interactions", priority: 6),
]),
]
)

static let restAPI = ProjectTemplate(
id: "rest-api",
name: "REST API",
description: "Backend API service with authentication, database, and documentation",
icon: "server.rack",
techStack: "Node.js, Express, PostgreSQL",
projectType: .software,
features: [
TemplateFeature(name: "API Endpoints", description: "RESTful API design and implementation", tasks: [
TemplateTask(agentType: .analyzer, title: "Design API schema", description: "Define data models, relationships, and API endpoint structure", priority: 10),
TemplateTask(agentType: .coder, title: "Implement API endpoints", description: "Build all REST endpoints with validation and error handling", priority: 9),
TemplateTask(agentType: .coder, title: "Set up database and migrations", description: "Configure database connection, create schemas, and seed data", priority: 9),
]),
TemplateFeature(name: "Auth & Security", description: "API authentication and security", tasks: [
TemplateTask(agentType: .coder, title: "Implement JWT authentication", description: "Add auth middleware, token generation, and refresh logic", priority: 8),
TemplateTask(agentType: .coder, title: "Add rate limiting and security headers", description: "Configure rate limiting, CORS, helmet, and input sanitization", priority: 7),
]),
TemplateFeature(name: "Testing & Docs", description: "API tests and documentation", tasks: [
TemplateTask(agentType: .tester, title: "Write API tests", description: "Create integration tests for all endpoints with edge cases", priority: 7),
TemplateTask(agentType: .contentWriter, title: "Generate API documentation", description: "Create OpenAPI/Swagger docs with examples for all endpoints", priority: 5),
]),
]
)

static let landingPage = ProjectTemplate(
id: "landing-page",
name: "Landing Page",
description: "Marketing landing page with responsive design, SEO optimization, and analytics",
icon: "doc.richtext",
techStack: "HTML, CSS, JavaScript",
projectType: .content,
features: [
TemplateFeature(name: "Design & Layout", description: "Visual design and responsive layout", tasks: [
TemplateTask(agentType: .designer, title: "Design landing page layout", description: "Create hero section, features, testimonials, CTA, and footer sections", priority: 9),
TemplateTask(agentType: .coder, title: "Implement responsive layout", description: "Build the landing page with mobile-first responsive design", priority: 8),
]),
TemplateFeature(name: "Content & SEO", description: "Copywriting and search optimization", tasks: [
TemplateTask(agentType: .contentWriter, title: "Write landing page copy", description: "Create compelling headlines, feature descriptions, and CTAs", priority: 8),
TemplateTask(agentType: .coder, title: "Optimize for SEO", description: "Add meta tags, structured data, sitemap, and performance optimizations", priority: 6),
]),
TemplateFeature(name: "Deploy", description: "Publish the landing page", tasks: [
TemplateTask(agentType: .devops, title: "Deploy landing page", description: "Set up hosting and deploy the landing page", priority: 5),
]),
]
)

static let blogCMS = ProjectTemplate(
id: "blog-cms",
name: "Blog / CMS",
description: "Content management system with blog, categories, and multi-channel publishing",
icon: "newspaper",
techStack: "Next.js, MDX, Tailwind CSS",
projectType: .content,
features: [
TemplateFeature(name: "Content System", description: "Blog post management and rendering", tasks: [
TemplateTask(agentType: .coder, title: "Build blog engine", description: "Create MDX-based blog with categories, tags, and search", priority: 9),
TemplateTask(agentType: .coder, title: "Implement admin interface", description: "Create post editor, media library, and settings panel", priority: 8),
]),
TemplateFeature(name: "Design", description: "Blog theme and components", tasks: [
TemplateTask(agentType: .designer, title: "Design blog theme", description: "Create layout, typography, and component designs for blog", priority: 8),
TemplateTask(agentType: .coder, title: "Implement blog theme", description: "Build responsive blog theme with dark mode support", priority: 7),
]),
TemplateFeature(name: "Publishing", description: "SEO and content distribution", tasks: [
TemplateTask(agentType: .contentWriter, title: "Write initial content", description: "Create initial blog posts and about page content", priority: 6),
TemplateTask(agentType: .coder, title: "Add SEO and RSS", description: "Implement SEO meta tags, RSS feed, and sitemap", priority: 5),
]),
]
)

static let cliTool = ProjectTemplate(
id: "cli-tool",
name: "CLI Tool",
description: "Command-line tool with argument parsing, subcommands, and documentation",
icon: "terminal",
techStack: "Python, Click",
projectType: .software,
features: [
TemplateFeature(name: "Core Logic", description: "Main functionality and commands", tasks: [
TemplateTask(agentType: .analyzer, title: "Design CLI architecture", description: "Define command structure, arguments, and output formats", priority: 10),
TemplateTask(agentType: .coder, title: "Implement core commands", description: "Build main CLI commands with argument parsing and validation", priority: 9),
TemplateTask(agentType: .coder, title: "Add configuration management", description: "Implement config file loading, defaults, and environment variables", priority: 7),
]),
TemplateFeature(name: "Testing", description: "Unit and integration tests", tasks: [
TemplateTask(agentType: .tester, title: "Write CLI tests", description: "Create tests for all commands with various argument combinations", priority: 7),
]),
TemplateFeature(name: "Documentation", description: "User docs and packaging", tasks: [
TemplateTask(agentType: .contentWriter, title: "Write CLI documentation", description: "Create README, usage examples, and installation guide", priority: 5),
TemplateTask(agentType: .devops, title: "Set up packaging and distribution", description: "Configure package build, versioning, and distribution (PyPI/Homebrew)", priority: 4),
]),
]
)
}
35 changes: 35 additions & 0 deletions CreedFlow/Sources/CreedFlow/Models/TaskComment.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation
import GRDB

package struct TaskComment: Codable, Identifiable, Equatable {
package var id: UUID
package var taskId: UUID
package var content: String
package var author: Author
package var createdAt: Date

package enum Author: String, Codable, DatabaseValueConvertible {
case user
case system
}

package init(
id: UUID = UUID(),
taskId: UUID,
content: String,
author: Author = .user,
createdAt: Date = Date()
) {
self.id = id
self.taskId = taskId
self.content = content
self.author = author
self.createdAt = createdAt
}
}

// MARK: - Persistence

extension TaskComment: FetchableRecord, PersistableRecord {
package static let databaseTableName = "taskComment"
}
Loading