Skip to content
Closed
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
16 changes: 14 additions & 2 deletions .claude/templates/coding_prompt.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,27 @@ tail -500 claude-progress.txt

# 5. Check recent git history
git log --oneline -20

# 6. Check for knowledge files (additional project context/requirements)
ls -la knowledge/ 2>/dev/null || echo "No knowledge directory"
```

**IMPORTANT:** If a `knowledge/` directory exists, read all `.md` files in it.
These contain additional project context, requirements documents, research notes,
or reference materials that will help you understand the project better.

```bash
# Read all knowledge files if the directory exists
for f in knowledge/*.md; do [ -f "$f" ] && echo "=== $f ===" && cat "$f"; done 2>/dev/null
```

Then use MCP tools to check feature status:

```
# 6. Get progress statistics (passing/total counts)
# 7. Get progress statistics (passing/total counts)
Use the feature_get_stats tool

# 7. Get the next feature to work on
# 8. Get the next feature to work on
Use the feature_get_next tool
```

Expand Down
19 changes: 19 additions & 0 deletions .claude/templates/initializer_prompt.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ Start by reading `app_spec.txt` in your working directory. This file contains
the complete specification for what you need to build. Read it carefully
before proceeding.

### SECOND: Check for Knowledge Files

Check if there's a `knowledge/` directory with additional context:

```bash
ls -la knowledge/ 2>/dev/null || echo "No knowledge directory"
```

**IMPORTANT:** If a `knowledge/` directory exists, read ALL `.md` files in it.
These contain additional project context, requirements documents, research notes,
or reference materials that provide deeper understanding of the project.

```bash
# Read all knowledge files if the directory exists
for f in knowledge/*.md; do [ -f "$f" ] && echo "=== $f ===" && cat "$f"; done 2>/dev/null
```

Use this information alongside `app_spec.txt` when creating features.

---

## REQUIRED FEATURE COUNT
Expand Down
137 changes: 137 additions & 0 deletions server/routers/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
from fastapi import APIRouter, HTTPException

from ..schemas import (
KnowledgeFile,
KnowledgeFileContent,
KnowledgeFileList,
KnowledgeFileUpload,
ProjectCreate,
ProjectDetail,
ProjectPrompts,
Expand Down Expand Up @@ -355,3 +359,136 @@ async def get_project_stats_endpoint(name: str):
raise HTTPException(status_code=404, detail="Project directory not found")

return get_project_stats(project_dir)


def get_knowledge_dir(project_dir: Path) -> Path:
"""Get the knowledge directory for a project."""
return project_dir / "knowledge"


@router.get("/{name}/knowledge", response_model=KnowledgeFileList)
async def list_knowledge_files(name: str):
"""List all knowledge files for a project."""
_init_imports()
_, _, get_project_path, _, _ = _get_registry_functions()

name = validate_project_name(name)
project_dir = get_project_path(name)

if not project_dir:
raise HTTPException(status_code=404, detail=f"Project '{name}' not found")

if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found")

knowledge_dir = get_knowledge_dir(project_dir)

if not knowledge_dir.exists():
return KnowledgeFileList(files=[], count=0)

files = []
for filepath in knowledge_dir.glob("*.md"):
if filepath.is_file():
stat = filepath.stat()
from datetime import datetime
files.append(KnowledgeFile(
name=filepath.name,
size=stat.st_size,
modified=datetime.fromtimestamp(stat.st_mtime)
))
Comment on lines +389 to +398
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Move import outside the loop.

The from datetime import datetime import on line 393 is inside the loop, causing repeated import overhead. Move it to the top of the file or function.

♻️ Suggested fix

Move the import to the top of the file with other imports:

 import re
 import shutil
 import sys
+from datetime import datetime
 from pathlib import Path

Then remove line 393.

🤖 Prompt for AI Agents
In @server/routers/projects.py around lines 389 - 398, The import "from datetime
import datetime" is inside the loop that builds files from
knowledge_dir.glob("*.md"), causing repeated import overhead; move that import
out of the loop (preferably to the module top with other imports) and remove the
in-loop import statement so the loop that appends
KnowledgeFile(name=filepath.name, size=stat.st_size,
modified=datetime.fromtimestamp(stat.st_mtime)) uses the already-imported
datetime.


# Sort by name
files.sort(key=lambda f: f.name.lower())

return KnowledgeFileList(files=files, count=len(files))


@router.get("/{name}/knowledge/{filename}", response_model=KnowledgeFileContent)
async def get_knowledge_file(name: str, filename: str):
"""Get the content of a specific knowledge file."""
_init_imports()
_, _, get_project_path, _, _ = _get_registry_functions()

name = validate_project_name(name)
project_dir = get_project_path(name)

if not project_dir:
raise HTTPException(status_code=404, detail=f"Project '{name}' not found")

if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found")

# Validate filename (prevent path traversal)
if not re.match(r'^[a-zA-Z0-9_\-\.]+\.md$', filename):
raise HTTPException(status_code=400, detail="Invalid filename")

knowledge_dir = get_knowledge_dir(project_dir)
filepath = knowledge_dir / filename

if not filepath.exists():
raise HTTPException(status_code=404, detail=f"Knowledge file '{filename}' not found")

try:
content = filepath.read_text(encoding="utf-8")
return KnowledgeFileContent(name=filename, content=content)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to read file: {e}")


@router.post("/{name}/knowledge", response_model=KnowledgeFileContent)
async def upload_knowledge_file(name: str, file: KnowledgeFileUpload):
"""Upload a knowledge file to a project."""
_init_imports()
_, _, get_project_path, _, _ = _get_registry_functions()

name = validate_project_name(name)
project_dir = get_project_path(name)

if not project_dir:
raise HTTPException(status_code=404, detail=f"Project '{name}' not found")

if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found")

knowledge_dir = get_knowledge_dir(project_dir)
knowledge_dir.mkdir(parents=True, exist_ok=True)

filepath = knowledge_dir / file.filename

try:
filepath.write_text(file.content, encoding="utf-8")
return KnowledgeFileContent(name=file.filename, content=file.content)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to write file: {e}")


@router.delete("/{name}/knowledge/{filename}")
async def delete_knowledge_file(name: str, filename: str):
"""Delete a knowledge file from a project."""
_init_imports()
_, _, get_project_path, _, _ = _get_registry_functions()

name = validate_project_name(name)
project_dir = get_project_path(name)

if not project_dir:
raise HTTPException(status_code=404, detail=f"Project '{name}' not found")

if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found")

# Validate filename (prevent path traversal)
if not re.match(r'^[a-zA-Z0-9_\-\.]+\.md$', filename):
raise HTTPException(status_code=400, detail="Invalid filename")

knowledge_dir = get_knowledge_dir(project_dir)
filepath = knowledge_dir / filename

if not filepath.exists():
raise HTTPException(status_code=404, detail=f"Knowledge file '{filename}' not found")

try:
filepath.unlink()
return {"success": True, "message": f"Deleted '{filename}'"}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete file: {e}")
29 changes: 29 additions & 0 deletions server/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,35 @@ class CreateDirectoryRequest(BaseModel):
name: str = Field(..., min_length=1, max_length=255)


# ============================================================================
# Knowledge File Schemas
# ============================================================================

class KnowledgeFile(BaseModel):
"""Information about a knowledge file."""
name: str
size: int # Bytes
modified: datetime


class KnowledgeFileList(BaseModel):
"""Response containing list of knowledge files."""
files: list[KnowledgeFile]
count: int


class KnowledgeFileContent(BaseModel):
"""Response containing knowledge file content."""
name: str
content: str


class KnowledgeFileUpload(BaseModel):
"""Request schema for uploading a knowledge file."""
filename: str = Field(..., min_length=1, max_length=255, pattern=r'^[a-zA-Z0-9_\-\.]+\.md$')
content: str = Field(..., min_length=1)


# ============================================================================
# Settings Schemas
# ============================================================================
Expand Down
Loading