Project Module
Slug: project
Overview
Projects belong to workspaces and serve as containers for tasks. A project can link to a Git repository or a local directory. The project type (frontend, backend, etc.) affects which features are available — for example, preview is only available for frontend projects. When importing a local project, Tower auto-detects the Git remote URL. The "Generate Description" button uses AI to analyze the project's directory structure and produce a structured description automatically.
Details
- Two project types:
NORMALandGIT. The type is automatically derived from whether agitUrlis provided — you cannot set it manually. - Local import: Point Tower at a local directory path. Git information (remote URL, current branch) is detected automatically.
- AI-powered analysis: The "Generate Description" button on the project detail/import page scans the project's
localPathand generates a structured description covering tech stack, architecture, and key directories. - Repository linking: Projects can have multiple associated repositories for broader context.
- Product group: Group the multiple repos of one product (frontend / backend / trace static-knowledge / requirements) so the knowledge base searches them together.
New / Import Project Dialog Options
Both dialogs (create-project-dialog / import-project-dialog) share the same fields:
- Workspace: shown as a dropdown when more than one workspace exists; defaults to the currently highlighted workspace. Switching workspace clears the selected group.
- Group: after a workspace is chosen, a
GroupSelectdropdown offers "no group / existing group / + new group"; the new-group option creates and selects a group inline. - Project type:
FRONTEND/BACKEND(used as knowledge-base grouping semantics). - gitUrl / localPath: create is Git-URL-first (auto-derives project name and local path, with a "Clone" button); import is folder-first (browse the folder, then the git remote is auto-detected).
Note: The group is not created together with the project —
createProjectdoes not acceptgroupId. The dialog creates the project first, then callssetProjectGroup(projectId, groupId)to bind it.
Data Model
ProductGroup (id, name, description?) // per-workspace product group; name unique per workspace
└── Project[]
Project (id, name, alias?, description?, type, gitUrl?, localPath?, groupId?, knowledgeDir?)
├── Task[]
├── Repository[]
└── ProjectFact[] (key, value) // structured fact cards; precise-match knowledge sourcetype:NORMAL|GIT, automatically derived from whethergitUrlis present; cannot be set manuallyworkspaceId: FK to Workspace, cascade deletegroupId?: FK to ProductGroup,onDelete: SetNull— deleting a group unbinds its members without deleting themknowledgeDir?: overrides the in-repo knowledge dir (defaultdocs/知识库)
Product Group
A ProductGroup is a first-class entity under a workspace that groups the multiple repos of one product for cross-repo knowledge search.
- Concept: A product is often split into several repos (frontend / backend / trace static-knowledge / requirements). Once grouped, asking
ask_project_knowledgeabout any member automatically pulls in all sibling projects of the same group. - Create: pick "+ new group" in the group dropdown of the new/import dialog, or use MCP
create_product_group/ server actioncreateProductGroup. Group name is unique per workspace. - Assign:
setProjectGroup(projectId, groupId)(UI) or MCPupdate_projectwithgroupId; passnull/""to detach. - Same-workspace constraint: all members of a group must belong to the same workspace. Because the knowledge layer aggregates purely by
groupId(no workspace filter), a cross-workspace member would leak knowledge across workspaces — so assignment enforces same-workspace.
Knowledge Base
ask_project_knowledge (src/lib/knowledge.ts) aggregates four sources; Tower only retrieves and aggregates — the calling LLM composes the final answer:
- In-repo knowledge files:
<localPath>/<knowledgeDir>/*.md(defaultdocs/知识库, overridable viaknowledgeDir, with a directory-escape guard) - Fact cards
ProjectFact: machine-underivable project-level key-values (production/CICD paths, domains…), managed viamanage_project_facts - Versions and merge commits:
Version+ each task'smergeCommit/branch/changelog - DB notes:
ProjectNote(FTS full-text search)
If a project has a groupId, the search scope expands to all projects in the same group.
File Reference
Server Actions (src/actions/)
| File | Function | Description |
|---|---|---|
workspace-actions.ts | createProject, updateProject, deleteProject | Project CRUD (createProject does not accept groupId) |
workspace-actions.ts | getProjectByLocalPath(path) | Find by local path |
workspace-actions.ts | getRecentLocalProjects(limit?) | Recent local projects |
group-actions.ts | getProductGroups, createProductGroup, updateProductGroup, deleteProductGroup, setProjectGroup | Product-group CRUD and project↔group assignment |
project-actions.ts | Project analysis functions | Project description generation, etc. |
Components (src/components/project/)
| Component | Description |
|---|---|
create-project-dialog.tsx | Create project dialog (select workspace / group / project type) |
import-project-dialog.tsx | Import project dialog (detect git remote, migrate path) |
group-select.tsx | Group dropdown selector (no group / existing / inline new group) |
MCP Tools (src/mcp/tools/project-tools.ts)
list_projects/create_project/update_project/delete_project/identify_projectlist_product_groups/create_product_group— product groupsupdate_projectsupportsgroupId(""detaches) andknowledgeDir
Knowledge-base tools live in src/mcp/tools/knowledge-base-tools.ts: ask_project_knowledge / manage_project_facts.
Constraints
typefield is read-only, determined by the presence ofgitUrl- Deleting a project cascades to all its Tasks
- A group and its member projects must share one workspace;
createProjectdoes not acceptgroupId— bind separately withsetProjectGroupafter creation - Deleting a group only unbinds members (
groupId = null), it does not delete the projects
