1"""Team Knowledge Base module for shared reflection database with permissions.

2

3 This module provides team collaboration features including:

4- Shared reflection database with user permissions

5- Team access control and role management

6- Collaborative knowledge sharing

7- Team-specific conversation organization

8"""

9

10 import hashlib

11 import importlib.util

12 import json

13 import logging

14 import sqlite3

15 import threading

16 import time

17 from dataclasses import asdict, dataclass

18 from datetime import datetime, timedelta

19 from enum import Enum

20 from pathlib import Path

21 from typing import Any

22

23 DUCKDB_AVAILABLE = importlib.util.find_spec("duckdb") is not None

24 AIOFILES_AVAILABLE = importlib.util.find_spec("aiofiles") is not None

25

26 logger = logging.getLogger(__name__)

27

28

29 class UserRole(Enum):

30 """User roles in team knowledge base."""

31

32 VIEWER = "viewer"

33 CONTRIBUTOR = "contributor"

34 MODERATOR = "moderator"

35 ADMIN = "admin"

36

37

38 class AccessLevel(Enum):

39 """Access levels for knowledge base content."""

40

41 PRIVATE = "private"

42 TEAM = "team"

43 PROJECT = "project"

44 PUBLIC = "public"

45

46

47@dataclass

48 class TeamUser:

49 """Team user information."""

50

51 user_id: str

52 username: str

53 email: str | None

54 role: UserRole

55 teams: list[str]

56 created_at: datetime

57 last_active: datetime

58 permissions: dict[str, bool]

59

60

61@dataclass

62 class TeamReflection:

63 """Team-shared reflection with access control."""

64

65 id: str

66 content: str

67 tags: list[str]

68 access_level: AccessLevel

69 team_id: str | None

70 project_id: str | None

71 author_id: str

72 created_at: datetime

73 updated_at: datetime

74 votes: int

75 viewers: set[str]

76 editors: set[str]

77

78

79@dataclass

80 class Team:

81 """Team information and configuration."""

82

83 team_id: str

84 name: str

85 description: str

86 owner_id: str

87 members: set[str]

88 projects: set[str]

89 created_at: datetime

90 settings: dict[str, Any]

91

92

93 class TeamKnowledgeManager:

94 """Manages team knowledge base with permissions and access control."""

95

96 def __init__(self, db_path: str | None = None) -> None:

97 """Initialize team knowledge manager."""

98 self.db_path = db_path or str(

99 Path.home() / ".claude" / "data" / "team_knowledge.db",

100 )

101 self._lock = threading.Lock()

102 self._init_database()

103

104 def _init_database(self) -> None:

105 """Initialize SQLite database for team knowledge."""

106 Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)

107

108 with sqlite3.connect(self.db_path) as conn:

109 self._create_tables(conn)

110 self._create_indices(conn)

111

112 def _create_tables(self, conn: sqlite3.Connection) -> None:

113 """Create database tables."""

114 self._create_users_table(conn)

115 self._create_teams_table(conn)

116 self._create_reflections_table(conn)

117 self._create_access_logs_table(conn)

118

119 def _create_users_table(self, conn: sqlite3.Connection) -> None:

120 """Create users table."""

121 conn.execute("""

122 CREATE TABLE IF NOT EXISTS users (

123 user_id TEXT PRIMARY KEY,

124 username TEXT UNIQUE NOT NULL,

125 email TEXT,

126 role TEXT NOT NULL,

127 teams TEXT, -- JSON array

128 created_at TIMESTAMP,

129 last_active TIMESTAMP,

130 permissions TEXT -- JSON object

131 )

132 """)

133

134 def _create_teams_table(self, conn: sqlite3.Connection) -> None:

135 """Create teams table."""

136 conn.execute("""

137 CREATE TABLE IF NOT EXISTS teams (

138 team_id TEXT PRIMARY KEY,

139 name TEXT NOT NULL,

140 description TEXT,

141 owner_id TEXT NOT NULL,

142 members TEXT, -- JSON array

143 projects TEXT, -- JSON array

144 created_at TIMESTAMP,

145 settings TEXT, -- JSON object

146 FOREIGN KEY (owner_id) REFERENCES users(user_id)

147 )

148 """)

149

150 def _create_reflections_table(self, conn: sqlite3.Connection) -> None:

151 """Create team_reflections table."""

152 conn.execute("""

153 CREATE TABLE IF NOT EXISTS team_reflections (

154 id TEXT PRIMARY KEY,

155 content TEXT NOT NULL,

156 tags TEXT, -- JSON array

157 access_level TEXT NOT NULL,

158 team_id TEXT,

159 project_id TEXT,

160 author_id TEXT NOT NULL,

161 created_at TIMESTAMP,

162 updated_at TIMESTAMP,

163 votes INTEGER DEFAULT 0,

164 viewers TEXT, -- JSON array

165 editors TEXT, -- JSON array

166 FOREIGN KEY (author_id) REFERENCES users(user_id),

167 FOREIGN KEY (team_id) REFERENCES teams(team_id)

168 )

169 """)

170

171 def _create_access_logs_table(self, conn: sqlite3.Connection) -> None:

172 """Create access_logs table."""

173 conn.execute("""

174 CREATE TABLE IF NOT EXISTS access_logs (

175 id INTEGER PRIMARY KEY AUTOINCREMENT,

176 user_id TEXT NOT NULL,

177 action TEXT NOT NULL,

178 resource_id TEXT,

179 resource_type TEXT,

180 timestamp TIMESTAMP,

181 details TEXT -- JSON object

182 )

183 """)

184

185 def _create_indices(self, conn: sqlite3.Connection) -> None:

186 """Create database indices."""

187 indices = [

188 "CREATE INDEX IF NOT EXISTS idx_reflections_team ON team_reflections(team_id)",

189 "CREATE INDEX IF NOT EXISTS idx_reflections_project ON team_reflections(project_id)",

190 "CREATE INDEX IF NOT EXISTS idx_reflections_author ON team_reflections(author_id)",

191 "CREATE INDEX IF NOT EXISTS idx_reflections_access ON team_reflections(access_level)",

192 "CREATE INDEX IF NOT EXISTS idx_access_logs_user ON access_logs(user_id)",

193 "CREATE INDEX IF NOT EXISTS idx_access_logs_timestamp ON access_logs(timestamp)",

194 ]

195

196 for index_sql in indices:

197 conn.execute(index_sql)

198

199 async def create_user(

200 self,

201 user_id: str,

202 username: str,

203 email: str | None = None,

204 role: UserRole = UserRole.CONTRIBUTOR,

205 ) -> TeamUser:

206 """Create a new team user."""

207 user = TeamUser(

208 user_id=user_id,

209 username=username,

210 email=email,

211 role=role,

212 teams=[],

213 created_at=datetime.now(),

214 last_active=datetime.now(),

215 permissions=self._get_default_permissions(role),

216 )

217

218 with sqlite3.connect(self.db_path) as conn:

219 conn.execute(

220 """

221 INSERT INTO users (user_id, username, email, role, teams, created_at, last_active, permissions)

222 VALUES (?, ?, ?, ?, ?, ?, ?, ?)

223 """,

224 (

225 user.user_id,

226 user.username,

227 user.email,

228 user.role.value,

229 json.dumps(user.teams),

230 user.created_at,

231 user.last_active,

232 json.dumps(user.permissions),

233 ),

234 )

235

236 await self._log_access(

237 user_id,

238 "user_created",

239 user_id,

240 "user",

241 {"role": role.value},

242 )

243 return user

244

245 async def create_team(

246 self,

247 team_id: str,

248 name: str,

249 description: str,

250 owner_id: str,

251 ) -> Team:

252 """Create a new team."""

253 team = Team(

254 team_id=team_id,

255 name=name,

256 description=description,

257 owner_id=owner_id,

258 members={owner_id},

259 projects=set(),

260 created_at=datetime.now(),

261 settings={"auto_approve_members": False, "public_reflections": True},

262 )

263

264 with sqlite3.connect(self.db_path) as conn:

265 conn.execute(

266 """

267 INSERT INTO teams (team_id, name, description, owner_id, members, projects, created_at, settings)

268 VALUES (?, ?, ?, ?, ?, ?, ?, ?)

269 """,

270 (

271 team.team_id,

272 team.name,

273 team.description,

274 team.owner_id,

275 json.dumps(list(team.members)),

276 json.dumps(list(team.projects)),

277 team.created_at,

278 json.dumps(team.settings),

279 ),

280 )

281

282 # Add owner to team

283 await self._add_user_to_team(owner_id, team_id)

284 await self._log_access(

285 owner_id,

286 "team_created",

287 team_id,

288 "team",

289 {"name": name},

290 )

291 return team

292

293 async def add_team_reflection(

294 self,

295 content: str,

296 author_id: str,

297 tags: list[str] | None = None,

298 access_level: AccessLevel = AccessLevel.TEAM,

299 team_id: str | None = None,

300 project_id: str | None = None,

301 ) -> str:

302 """Add reflection to team knowledge base."""

303 reflection_id = hashlib.sha256(

304 f"{content}{author_id}{time.time()}".encode(),

305 ).hexdigest()[:16]

306

307 reflection = TeamReflection(

308 id=reflection_id,

309 content=content,

310 tags=tags or [],

311 access_level=access_level,

312 team_id=team_id,

313 project_id=project_id,

314 author_id=author_id,

315 created_at=datetime.now(),

316 updated_at=datetime.now(),

317 votes=0,

318 viewers=set(),

319 editors=set(),

320 )

321

322 with sqlite3.connect(self.db_path) as conn:

323 conn.execute(

324 """

325 INSERT INTO team_reflections

326 (id, content, tags, access_level, team_id, project_id, author_id, created_at, updated_at, votes, viewers, editors)

327 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

328 """,

329 (

330 reflection.id,

331 reflection.content,

332 json.dumps(reflection.tags),

333 reflection.access_level.value,

334 reflection.team_id,

335 reflection.project_id,

336 reflection.author_id,

337 reflection.created_at,

338 reflection.updated_at,

339 reflection.votes,

340 json.dumps(list(reflection.viewers)),

341 json.dumps(list(reflection.editors)),

342 ),

343 )

344

345 await self._log_access(

346 author_id,

347 "reflection_created",

348 reflection_id,

349 "reflection",

350 {"team_id": team_id, "access_level": access_level.value},

351 )

352 return reflection_id

353

354 async def search_team_reflections(

355 self,

356 query: str,

357 user_id: str,

358 team_id: str | None = None,

359 project_id: str | None = None,

360 tags: list[str] | None = None,

361 limit: int = 20,

362 ) -> list[dict[str, Any]]:

363 """Search team reflections with access control."""

364 user_teams = await self._get_user_teams(user_id)

365

366 with sqlite3.connect(self.db_path) as conn:

367 conn.row_factory = sqlite3.Row

368

369 query_builder = self._build_search_query(

370 user_teams,

371 user_id,

372 query,

373 team_id,

374 project_id,

375 tags,

376 limit,

377 )

378 cursor = conn.execute(query_builder.sql, query_builder.params)

379 results = self._process_search_results(cursor.fetchall())

380

381 await self._log_access(

382 user_id,

383 "reflections_searched",

384 None,

385 "search",

386 {"query": query, "results_count": len(results)},

387 )

388 return results

389

390 @dataclass(frozen=True)

391 class _SearchQueryBuilder:

392 """Immutable search query builder result."""

393

394 sql: str

395 params: list[str | int]

396

397 def _build_search_query(

398 self,

399 user_teams: list[str],

400 user_id: str,

401 query: str,

402 team_id: str | None,

403 project_id: str | None,

404 tags: list[str] | None,

405 limit: int,

406 ) -> _SearchQueryBuilder:

407 """Build parameterized search query with conditions."""

408 where_conditions = ["1=1"]

409 params: list[str | int] = []

410

411 # Add access control conditions

412 access_condition, access_params = self._build_access_condition(

413 user_teams,

414 user_id,

415 )

416 where_conditions.append(access_condition)

417 params.extend(access_params)

418

419 # Add content search conditions

420 if query:

421 where_conditions.append("(content LIKE ? OR tags LIKE ?)")

422 params.extend([f"%{query}%", f"%{query}%"])

423

424 # Add filter conditions

425 self._add_filter_conditions(where_conditions, params, team_id, project_id, tags)

426

427 # Build SQL safely - all user input is parameterized via params list

428 query_sql = (

429 "SELECT * FROM team_reflections WHERE "

430 + " AND ".join(where_conditions)

431 + " ORDER BY votes DESC, created_at DESC LIMIT ?"

432 )

433 params.append(limit)

434

435 return self._SearchQueryBuilder(sql=query_sql, params=params)

436

437 def _build_access_condition(

438 self,

439 user_teams: list[str],

440 user_id: str,

441 ) -> tuple[str, list[str]]:

442 """Build access control condition for query."""

443 placeholders = ",".join("?" * len(user_teams))

444 access_condition = f"""(

445 access_level = 'public' OR

446 (access_level = 'team' AND team_id IN ({placeholders}) AND team_id IS NOT NULL) OR

447 author_id = ?

448 )"""

449 access_params = [*user_teams, user_id]

450 return access_condition, access_params

451

452 def _add_filter_conditions(

453 self,

454 where_conditions: list[str],

455 params: list[str | int],

456 team_id: str | None,

457 project_id: str | None,

458 tags: list[str] | None,

459 ) -> None:

460 """Add filter conditions to query builder."""

461 if team_id:

462 where_conditions.append("team_id = ?")

463 params.append(team_id)

464

465 if project_id:

466 where_conditions.append("project_id = ?")

467 params.append(project_id)

468

469 if tags:

470 tag_conditions = []

471 for tag in tags:

472 params.append(f"%{tag}%") # type: ignore[func-returns-value]

473 tag_conditions.append("tags LIKE ?")

474 where_conditions.append(f"({' OR '.join(tag_conditions)})")

475

476 def _process_search_results(self, rows: list[sqlite3.Row]) -> list[dict[str, Any]]:

477 """Process and transform search results."""

478 return [

479 dict(row)

480 | {

481 "tags": json.loads(row["tags"] or "[]"),

482 "viewers": json.loads(row["viewers"] or "[]"),

483 "editors": json.loads(row["editors"] or "[]"),

484 }

485 for row in rows

486 ]

487

488 async def vote_reflection(

489 self,

490 reflection_id: str,

491 user_id: str,

492 vote_delta: int = 1,

493 ) -> bool:

494 """Vote on a team reflection."""

495 if not await self._can_access_reflection(reflection_id, user_id):

496 return False

497

498 with sqlite3.connect(self.db_path) as conn:

499 conn.execute(

500 """

501 UPDATE team_reflections

502 SET votes = votes + ?, updated_at = ?

503 WHERE id = ?

504 """,

505 (vote_delta, datetime.now(), reflection_id),

506 )

507

508 await self._log_access(

509 user_id,

510 "reflection_voted",

511 reflection_id,

512 "reflection",

513 {"vote_delta": vote_delta},

514 )

515 return True

516

517 async def join_team(

518 self,

519 user_id: str,

520 team_id: str,

521 requester_id: str | None = None,

522 ) -> bool:

523 """Request to join a team or add user to team."""

524 team = await self._get_team(team_id)

525 if not team:

526 return False

527

528 # Check if requester has permission to add users

529 if requester_id and requester_id != user_id:

530 if not await self._can_manage_team(requester_id, team_id):

531 return False

532

533 await self._add_user_to_team(user_id, team_id)

534 await self._log_access(

535 user_id,

536 "team_joined",

537 team_id,

538 "team",

539 {"requester_id": requester_id},

540 )

541 return True

542

543 async def get_team_stats(self, team_id: str, user_id: str) -> dict[str, Any] | None:

544 """Get team statistics and activity."""

545 if not await self._can_access_team(user_id, team_id):

546 return None

547

548 with sqlite3.connect(self.db_path) as conn:

549 conn.row_factory = sqlite3.Row

550

551 # Get team info

552 team_row = conn.execute(

553 "SELECT * FROM teams WHERE team_id = ?",

554 (team_id,),

555 ).fetchone()

556 if not team_row:

557 return None

558

559 team_data = dict(team_row)

560 team_data["members"] = json.loads(team_data["members"] or "[]")

561 team_data["projects"] = json.loads(team_data["projects"] or "[]")

562

563 # Get reflection stats

564 reflection_stats = conn.execute(

565 """

566 SELECT

567 COUNT(*) as total_reflections,

568 COUNT(DISTINCT author_id) as active_contributors,

569 SUM(votes) as total_votes,

570 AVG(votes) as avg_votes

571 FROM team_reflections

572 WHERE team_id = ?

573 """,

574 (team_id,),

575 ).fetchone()

576

577 # Get recent activity

578 recent_activity = conn.execute(

579 """

580 SELECT COUNT(*) as recent_reflections

581 FROM team_reflections

582 WHERE team_id = ? AND created_at > ?

583 """,

584 (team_id, datetime.now() - timedelta(days=7)),

585 ).fetchone()

586

587 stats = {

588 "team": team_data.copy(),

589 "member_count": len(team_data["members"]),

590 "project_count": len(team_data["projects"]),

591 "reflection_stats": dict(reflection_stats),

592 "recent_activity": dict(recent_activity),

593 }

594

595 await self._log_access(user_id, "team_stats_viewed", team_id, "team", {})

596 return stats

597

598 async def get_user_permissions(self, user_id: str) -> dict[str, Any]:

599 """Get user's current permissions and team memberships."""

600 with sqlite3.connect(self.db_path) as conn:

601 conn.row_factory = sqlite3.Row

602 user_row = conn.execute(

603 "SELECT * FROM users WHERE user_id = ?",

604 (user_id,),

605 ).fetchone()

606

607 if not user_row:

608 return {}

609

610 user_data = dict(user_row)

611 user_data["teams"] = json.loads(user_data["teams"] or "[]")

612 user_data["permissions"] = json.loads(user_data["permissions"] or "{}")

613

614 # Get team details

615 team_details = []

616 if user_data["teams"]:

617 placeholders = ",".join("?" * len(user_data["teams"]))

618 # Build SQL safely - placeholders generated from list length, not user input

619 query = (

620 "SELECT team_id, name, description FROM teams WHERE team_id IN ("

621 + placeholders

622 + ")"

623 )

624 team_rows = conn.execute(query, user_data["teams"]).fetchall()

625 team_details = [dict(row) for row in team_rows]

626

627 return {

628 "user": user_data,

629 "teams": team_details,

630 "can_create_teams": user_data["permissions"].get("create_teams", False),

631 "can_moderate": user_data["permissions"].get("moderate_content", False),

632 }

633

634 # Private helper methods

635

636 def _get_default_permissions(self, role: UserRole) -> dict[str, bool]:

637 """Get default permissions for user role."""

638 base_permissions = {

639 "read_reflections": True,

640 "create_reflections": False,

641 "vote_reflections": False,

642 "join_teams": False,

643 "create_teams": False,

644 "moderate_content": False,

645 "admin_access": False,

646 }

647

648 if role == UserRole.CONTRIBUTOR:

649 base_permissions.update(

650 {

651 "create_reflections": True,

652 "vote_reflections": True,

653 "join_teams": True,

654 },

655 )

656 elif role == UserRole.MODERATOR:

657 base_permissions.update(

658 {

659 "create_reflections": True,

660 "vote_reflections": True,

661 "join_teams": True,

662 "create_teams": True,

663 "moderate_content": True,

664 },

665 )

666 elif role == UserRole.ADMIN:

667 base_permissions.update(dict.fromkeys(base_permissions.keys(), True))

668

669 return base_permissions

670

671 async def _add_user_to_team(self, user_id: str, team_id: str) -> None:

672 """Add user to team."""

673 with sqlite3.connect(self.db_path) as conn:

674 # Update team members

675 team_row = conn.execute(

676 "SELECT members FROM teams WHERE team_id = ?",

677 (team_id,),

678 ).fetchone()

679 if team_row:

680 members = set(json.loads(team_row[0] or "[]"))

681 members.add(user_id)

682 conn.execute(

683 "UPDATE teams SET members = ? WHERE team_id = ?",

684 (json.dumps(list(members)), team_id),

685 )

686

687 # Update user teams

688 user_row = conn.execute(

689 "SELECT teams FROM users WHERE user_id = ?",

690 (user_id,),

691 ).fetchone()

692 if user_row:

693 teams = json.loads(user_row[0] or "[]")

694 if team_id not in teams:

695 teams.append(team_id)

696 conn.execute(

697 "UPDATE users SET teams = ?, last_active = ? WHERE user_id = ?",

698 (json.dumps(teams), datetime.now(), user_id),

699 )

700

701 async def _get_user_teams(self, user_id: str) -> list[str]:

702 """Get teams user belongs to."""

703 with sqlite3.connect(self.db_path) as conn:

704 row = conn.execute(

705 "SELECT teams FROM users WHERE user_id = ?",

706 (user_id,),

707 ).fetchone()

708 return json.loads(row[0] or "[]") if row else []

709

710 async def _get_team(self, team_id: str) -> dict[str, Any] | None:

711 """Get team information."""

712 with sqlite3.connect(self.db_path) as conn:

713 conn.row_factory = sqlite3.Row

714 row = conn.execute(

715 "SELECT * FROM teams WHERE team_id = ?",

716 (team_id,),

717 ).fetchone()

718 if row:

719 team_data = dict(row)

720 team_data["members"] = set(json.loads(team_data["members"] or "[]"))

721 team_data["projects"] = set(json.loads(team_data["projects"] or "[]"))

722 team_data["settings"] = json.loads(team_data["settings"] or "{}")

723 return team_data

724 return None

725

726 async def _can_access_reflection(self, reflection_id: str, user_id: str) -> bool:

727 """Check if user can access reflection."""

728 with sqlite3.connect(self.db_path) as conn:

729 row = conn.execute(

730 """

731 SELECT access_level, team_id, author_id FROM team_reflections

732 WHERE id = ?

733 """,

734 (reflection_id,),

735 ).fetchone()

736

737 if not row:

738 return False

739

740 access_level, team_id, author_id = row

741

742 # Author can always access

743 if author_id == user_id:

744 return True

745

746 # Public reflections accessible to all

747 if access_level == AccessLevel.PUBLIC.value:

748 return True

749

750 # Team reflections require team membership

751 if access_level == AccessLevel.TEAM.value and team_id:

752 user_teams = await self._get_user_teams(user_id)

753 return team_id in user_teams

754

755 return False

756

757 async def _can_access_team(self, user_id: str, team_id: str) -> bool:

758 """Check if user can access team."""

759 user_teams = await self._get_user_teams(user_id)

760 return team_id in user_teams

761

762 async def _can_manage_team(self, user_id: str, team_id: str) -> bool:

763 """Check if user can manage team."""

764 team = await self._get_team(team_id)

765 if not team:

766 return False

767

768 # Team owner can manage

769 if team["owner_id"] == user_id:

770 return True

771

772 # Check if user has admin permissions

773 with sqlite3.connect(self.db_path) as conn:

774 row = conn.execute(

775 "SELECT permissions FROM users WHERE user_id = ?",

776 (user_id,),

777 ).fetchone()

778 if row:

779 permissions = json.loads(row[0] or "{}")

780 return bool(

781 permissions.get("admin_access", False)

782 or permissions.get(

783 "moderate_content",

784 False,

785 ),

786 )

787

788 return False

789

790 async def _log_access(

791 self,

792 user_id: str,

793 action: str,

794 resource_id: str | None,

795 resource_type: str,

796 details: dict[str, Any],

797 ) -> None:

798 """Log user access for audit trail."""

799 with sqlite3.connect(self.db_path) as conn:

800 conn.execute(

801 """

802 INSERT INTO access_logs (user_id, action, resource_id, resource_type, timestamp, details)

803 VALUES (?, ?, ?, ?, ?, ?)

804 """,

805 (

806 user_id,

807 action,

808 resource_id,

809 resource_type,

810 datetime.now(),

811 json.dumps(details),

812 ),

813 )

814

815

816# Global instance

817_team_knowledge_manager = None

818

819

820 def get_team_knowledge_manager() -> TeamKnowledgeManager:

821 """Get global team knowledge manager instance."""

822 global _team_knowledge_manager

823 if _team_knowledge_manager is None:

824 _team_knowledge_manager = TeamKnowledgeManager()

825 return _team_knowledge_manager

826

827

828# Public API functions for MCP tools

829 async def create_team_user(

830 user_id: str,

831 username: str,

832 email: str | None = None,

833 role: str = "contributor",

834) -> dict[str, Any]:

835 """Create a new team user."""

836 manager = get_team_knowledge_manager()

837 user_role = UserRole(role.lower())

838 user = await manager.create_user(user_id, username, email, user_role)

839 return asdict(user)

840

841

842 async def create_team(

843 team_id: str,

844 name: str,

845 description: str,

846 owner_id: str,

847) -> dict[str, Any]:

848 """Create a new team."""

849 manager = get_team_knowledge_manager()

850 team = await manager.create_team(team_id, name, description, owner_id)

851 return {

852 "team_id": team.team_id,

853 "name": team.name,

854 "description": team.description,

855 "owner_id": team.owner_id,

856 "member_count": len(team.members),

857 "project_count": len(team.projects),

858 "created_at": team.created_at.isoformat(),

859 "settings": team.settings,

860 }

861

862

863 async def add_team_reflection(

864 content: str,

865 author_id: str,

866 tags: list[str] | None = None,

867 access_level: str = "team",

868 team_id: str | None = None,

869 project_id: str | None = None,

870) -> str:

871 """Add reflection to team knowledge base."""

872 manager = get_team_knowledge_manager()

873 level = AccessLevel(access_level.lower())

874 return await manager.add_team_reflection(

875 content,

876 author_id,

877 tags,

878 level,

879 team_id,

880 project_id,

881 )

882

883

884 async def search_team_knowledge(

885 query: str,

886 user_id: str,

887 team_id: str | None = None,

888 project_id: str | None = None,

889 tags: list[str] | None = None,

890 limit: int = 20,

891) -> list[dict[str, Any]]:

892 """Search team reflections with access control."""

893 manager = get_team_knowledge_manager()

894 return await manager.search_team_reflections(

895 query,

896 user_id,

897 team_id,

898 project_id,

899 tags,

900 limit,

901 )

902

903

904 async def join_team(

905 user_id: str,

906 team_id: str,

907 requester_id: str | None = None,

908) -> bool:

909 """Join a team or add user to team."""

910 manager = get_team_knowledge_manager()

911 return await manager.join_team(user_id, team_id, requester_id)

912

913

914 async def get_team_statistics(team_id: str, user_id: str) -> dict[str, Any] | None:

915 """Get team statistics and activity."""

916 manager = get_team_knowledge_manager()

917 return await manager.get_team_stats(team_id, user_id)

918

919

920 async def get_user_team_permissions(user_id: str) -> dict[str, Any]:

921 """Get user's permissions and team memberships."""

922 manager = get_team_knowledge_manager()

923 return await manager.get_user_permissions(user_id)

924

925

926 async def vote_on_reflection(

927 reflection_id: str,

928 user_id: str,

929 vote_delta: int = 1,

930) -> bool:

931 """Vote on a team reflection."""

932 manager = get_team_knowledge_manager()

933 return await manager.vote_reflection(reflection_id, user_id, vote_delta)