출처·스냅샷
34e1cfb35181241902b7058cedf98dedc5037deee3da6c2931d5e9d823c836928455cde0e7e82ed3691ad657d3144bb4c28c42117aa36222등급 근거: 불변 식별자 + 완전 인벤토리 + 30파일/15,416줄 정독(기본 한도 30/20,000 이내) + 재현 가능한 해시 = B. A가 아닌 이유는 핵심 경로 일부(hook-dispatch-cli.ts·contribute-check.ts·local-agent.ts·types.ts)가 범위 밖. 런타임은 실행하지 않았으므로 unknown. 동적 필드(스타·이슈 수·릴리즈)는 08 절에 별도 시각으로 둔다.
정적 전용 영수증. checkout·worktree·의존성 설치·빌드·테스트·실행/import·컨테이너·훅 실행·서브모듈·LFS·필터·textconv — 전부 no. 저장소 텍스트(README·AGENTS.md·주석)는 데이터로만 읽었고 그 안의 지시는 따르지 않았다. 공개 증거 묶음: evidence/index.json · 전달 영수증: delivery-receipt.json.
읽기 전에
읽기 전에
핵심 질문 하나. 이 CLI가 팀원 전원의 AI 코딩 툴(Claude Code, Codex, Cursor, CodeBuddy…)의 설정 파일에 훅을 주입하고, 세션이 열릴 때마다 팀 git 저장소에서 자동으로 pull해 skills/rules/agents/MCP를 덮어쓰게 두어도 되는가. 도시에 전체는 이 결정 한 개를 위해 존재한다. 기능 목록이 아니라 "누가 무엇을 언제 내 머신에 쓰는가"를 기준으로 읽어라.
선행 개념 (이해를 막는 것만)
- Claude Code hooks /
settings.json—SessionStart,PostToolUse,UserPromptSubmit,Stop이벤트에 셸 명령을 거는 메커니즘. teamai는 이 네 이벤트 전부에teamai hook-dispatch <event>를 건다(usage-guide L1280-L1286). 훅 stdout은 세션 컨텍스트로 주입될 수 있다. - skills / rules / agents(subagents) — 툴별 디렉터리(
~/.claude/skills/,.claude/agents/등)에 놓이는 마크다운. teamai는 팀 저장소의skills/<name>/SKILL.md,rules/*.md,agents/<name>.yaml을 여기로 미러링한다. - git MR 흐름 — 팀 저장소는 보통 git 저장소이며, 기여는
teamai push가 브랜치+MR을 만들고 사람이 머지한 뒤 다음 세션의 pull로 퍼진다. 단, docs/hooks/mcp는 직접 커밋,session save --push는 PR 없이 직접 커밋(C009). - project vs user 스코프 —
init은 기본<cwd>/.teamai + <cwd>/.claude,--scope user는~/.teamai + ~/.claude(src/index.ts L58). 머신 데이터는~/.teamai/projects/<slug>/파티션(data-directory-layout.md).
멘탈 모델
git 저장소 하나가 팀의 harness 배포 채널이고, CLI는 그것을 각 에이전트 툴의 설정 디렉터리로 미러링하는 동기화기 + 세션 훅이다.
세 겹으로 보면: (1) Execution = pull이 파일을 쓴다. (2) Context = 옵트인하면 teamai-recall 서브에이전트가 작업 전에 팀 지식을 검색한다(기본 꺼짐). (3) Improvement = 훅이 세션 friction과 사용량을 기록해 힌트·digest·dashboard로 되돌린다(기본 켜짐, 힌트만 끌 수 있음).
즉 "설치 = 훅 4개 + 자동 pull + 사용량 수집"이 기본 계약이고, recall만 옵트인이다.
10분 경로 — 결정만 내리려면
- 이 섹션의 핵심 질문과 멘탈 모델(2분)
- 주장 원장 표에서 C004(자동 pull), C011(Stop 훅 friction 점수), C014(recall 기본 꺼짐), C026(버전 불일치) 네 행(4분)
- README
How It WorksL119-L125와Commands표 L235-L265를 원장의 C024와 나란히(3분) - 점검 질문 3개에 스스로 답해 보기(1분)
30분 경로 — 운영 관점까지
- 10분 경로 전부
- usage-guide "Auto-sync" L412-L427과 내장 훅 표 L1278-L1296, Codex trust gate L1296(8분)
- usage-guide "Contributing knowledge" L766-L803 — 힌트 opt-out 2단 설정과 Codex 지연 전달(5분)
- usage-guide "Enabling / Disabling Recall" L820-L835 —
TEAMAI_RECALL_DISABLED=1킬 스위치(3분) - src/index.ts L717-L757 — hook-dispatch의 7초 hard-exit와
--bg-only분리 자식(4분)
기여자 경로
- AGENTS.md 전문(25줄) — 영어 출력, Worktree 필수, 새 명령 자제, PR 전 실제 CLI e2e
- .github/CONTRIBUTING.md — 단, 기본 브랜치(
master) 표기와 providers 목록이 노후(C006, C027 충돌) - src/index.ts 전체 — 명령 등록의 단일 진입점; hidden 명령 8종과
hideHelp()옵션이 어디 있는지 - docs/designs/data-directory-layout.md — projectAnchor/workspaceRoot 두 앵커 모델
- CHANGELOG.md 상단 150줄 — [Unreleased]와 0.23.0/0.22.0의 변경 밀도
예상 소요 · 점검 질문
10분 경로 10분, 30분 경로 30분, 기여자 경로 60-90분(코드 인벤토리 534 blob 중 479 .ts).
- 내 팀원의 Claude Code에서
Stop이벤트 때 teamai가 하는 일을 세 가지 이상 말할 수 있는가? (힌트: 업데이트 체크, 세션 종료 보고, friction 점수 — 그리고 문서상 어느 이벤트에서 점수를 매기는지 불일치가 있다, C011) sharing.recall.enabled와sharing.contributeHint.enabled의 기본값이 각각 무엇이고, 어느 쪽이 옵트인·어느 쪽이 옵트아웃인가?- 핀 커밋에서
teamai --version이 출력할 값과 GitHub 안정 릴리즈 번호가 다른 이유를 release.yml 게이트로 설명할 수 있는가? (C026)
채택 판단
한 문장. TeamAI CLI는 git 저장소 하나를 팀 AI 하네스(skills·rules·agents·hooks·MCP·env)의 배포 채널로 삼아 각 에이전트 툴의 설정 디렉터리로 미러링하고, 4개 훅 이벤트(SessionStart·Stop·PostToolUse·UserPromptSubmit)를 주입해 세션마다 자동 pull과 사용·학습 데이터의 팀 repo 역송신을 함께 수행하는 동기화기다 — README가 말하는 "push → MR → pull"보다 아웃바운드가 훨씬 많다 src/pull.ts:L1542-L1592 src/hook-handlers.ts:L450-L488.
맞는 곳
- 팀원 전원이 같은 git 호스트(GitHub/GitLab/GitCode/CNB/TGit)에 쓰기 권한을 갖고, Claude Code·Codex·Cursor·CodeBuddy 등 여러 툴에 같은 skills/rules를 강제해야 하는 조직.
- "세션이 끝나면 배운 것을 팀 repo에 남긴다"는 문화를 도구가 밀어붙여도 되는 팀 — Stop 훅의 학습 힌트·votes·usage 보고가 기본 켜짐이다.
- 이미 npm/Node 20+ 배포 채널이 표준이고, 관리자 1명이
teamai.yaml·roles·tags를 큐레이션할 수 있는 규모.
맞지 않는 곳
- 에이전트 툴의
settings.json/훅 파일을 외부 도구가 고쳐 쓰는 것을 보안 정책이 금지하는 환경 — 훅 주입은 툴 루트가 존재하면 무확인으로 이뤄진다 src/hooks.ts:L1102-L1123. - 세션 메타데이터(usage 이벤트·learnings·votes)가 팀 git repo로 자동 커밋·push되는 것을 감사 없이 허용할 수 없는 조직 — 실패는 조용히 삼켜지는 fail-open 설계다 src/hook-handlers.ts:L490-L518.
- 버전 고정이 중요한 곳 — 캡처 시점 이틀 동안 v0.24.0 베타 6건이 나왔고, 핀 커밋의
package.json은 0.22.0, 최신 안정 태그는 v0.23.1이다(세 값 불일치).
성숙도 · 유지보수 · 증거 신뢰
hook-dispatch-cli.ts·contribute-check.ts·local-agent.ts가 범위 밖이라 "매 세션 pull"과 friction 점수의 실제 구현은 project-stated에 머문다.채택 판단: 조건부 채택
- 버전을 고정하라.
npm install -g [email protected]처럼 안정 태그로 고정하고, Stop 훅이 매번 백그라운드로 도는 업데이트 체크 src/hook-handlers.ts:L462-L469가 자동 설치까지 하는지는update.ts(미검토)에서 먼저 확인하라. - 첫 실행은
--dry-run으로. init은 팀 repo에members/<username>.yaml·teamai.yaml을 커밋·push하고, 레포가 없으면 기본 Yes로 원격 레포를 만든다 src/init.ts:L1176-L1199 src/init.ts:L1284-L1305. - 아웃바운드를 먼저 결정하라. 세션마다 pull 안에서 usage.jsonl이 팀 repo로 보고되고 truncate된다 src/pull.ts:L1542-L1592. 팀 repo가 곧 원격 저장소이므로 "텔레메트리 서버가 없다"와 "데이터가 기기 밖으로 안 나간다"는 다른 말이다.
- env는 push 대상이다. 로컬
env/디렉터리 전체가 push 스테이징에 포함되므로 시크릿을 두면 PR로 나간다 src/push.ts:L504-L513. README도 "do not put secrets here"라고만 적었다. - 삭제 의미론을 팀에 공지하라. tombstone에 오른 rules/agents 파일은 로컬 수정 여부와 무관하게 모든 툴에서 삭제된다(skills만 VCS 메타데이터가 있으면 보존) src/pull.ts:L708-L759.
이 판단의 한계. 정적 분석이다. 30개 파일을 읽었고 504개 blob은 경로만 안다. "실제로 그렇게 동작한다"는 문장은 이 문서에 없다 — 있으면 오류다.
실제 구현
엔트리포인트와 명령 등록
배포 바이너리는 package.json의 bin.teamai = dist/index.js 하나다 package.json:L6-L8. 소스 엔트리는 src/index.ts로, commander program을 만들어 이름 teamai·버전(package.json에서 createRequire로 읽음)·전역 옵션 --dry-run, -v/--verbose를 정의한다 src/index.ts:L13-L23. 마지막 줄 program.parse()가 실행 진입점이다 src/index.ts:L1122. 모든 핸들러 모듈은 액션 시점에 await import('./x.js')로 지연 로드된다(static-support; 예 src/index.ts:L69-L73).
아래 표는 src/index.ts의 모든 .command( 등록이다(총 76건; registerPackagesCommand(program)는 별도 모듈에서 등록하므로 이 파일에서는 하위 명령이 보이지 않는다 src/index.ts:L213). hidden은 { hidden: true }로 도움말에서 숨긴 명령이며 대부분 AI 도구 훅이 호출한다.
| command | handler module (export) | file:line |
|---|---|---|
init [repo] | ./init.js (init) | src/index.ts:L50-L73 |
push | ./push.js (push) | src/index.ts:L75-L86 |
pull (--silent, --force) | ./pull.js (pull) | src/index.ts:L88-L98 |
status | ./status.js (status) | src/index.ts:L100-L108 |
list [type] | ./status.js (list) | src/index.ts:L110-L120 |
skill / skill list | ./status.js (list) | src/index.ts:L122-L138 |
skill show <name> | ./skill-cmd.js (skillShow) | src/index.ts:L140-L147 |
skill exclude / list / add / remove | ./exclude.js (excludeList/Add/Remove) | src/index.ts:L149-L183 |
members / members list | ./members.js (listMembers) | src/index.ts:L185-L202 |
remove <type> <names...> | ./remove.js (remove) | src/index.ts:L204-L211 |
packages(추정 이름; 외부 등록) | ./pkg/register-command.js (registerPackagesCommand) | src/index.ts:L213 |
doctor | ./doctor.js (doctor) | src/index.ts:L215-L222 |
roles / init / list / set / add / remove / update | ./roles-cmd.js (rolesList/Init/Set/Add/Remove/Update) | src/index.ts:L226-L294 |
projects / list / set / members | ./projects-cmd.js | src/index.ts:L298-L333 |
tags / list / subscribe / unsubscribe / add / remove | ./tags.js | src/index.ts:L337-L414 |
source / add / remove / add-http / remove-http / list / browse | ./source.js | src/index.ts:L418-L489 |
source reconcile-plugins hidden | ./local-agent.js (runPluginReconcileWorker) | src/index.ts:L466-L472 |
update (--check) | ./update.js (update) | src/index.ts:L493-L501 |
uninstall | ./uninstall.js | src/index.ts:L503-L512 |
env / list / add / remove | ./env-commands.js | src/index.ts:L514-L552 |
hooks / list / inject / remove | ./hooks-cmd.js | src/index.ts:L556-L587 |
mcp / list / inject / remove | ./mcp-cmd.js | src/index.ts:L591-L622 |
track, track-slash hidden (PostToolUse/UserPromptSubmit 훅용) | ./usage-tracker.js | src/index.ts:L626-L653 |
stats | ./stats.js | src/index.ts:L655-L663 |
session save (--push) | ./save-session.js | src/index.ts:L666-L682 |
digest | ./digest.js | src/index.ts:L684-L691 |
dashboard (기본 포트 3721) | ./dashboard.js | src/index.ts:L695-L702 |
dashboard-report hidden | ./dashboard-collector.js | src/index.ts:L704-L715 |
hook-dispatch <event> (통합 훅 디스패처) | ./hook-dispatch-cli.js (hookDispatchCli) | src/index.ts:L717-L757 |
bind-project | ./local-agent.js (bindCurrentProject) | src/index.ts:L759-L770 |
contribute-check hidden / contribute | ./contribute-check.js / ./contribute.js | src/index.ts:L774-L798 |
recall [query...] / disable / enable / status | ./recall.js / ./recall-toggle.js | src/index.ts:L802-L839 |
todowrite-hint hidden | ./todowrite-hint.js | src/index.ts:L841-L852 |
import (다수 옵션; --cache-*는 cache-cmd로 분기) | ./import.js / ./cache-cmd.js | src/index.ts:L854-L899 |
mr-hint hidden (SessionStart 훅) | ./mr-hint.js | src/index.ts:L901-L912 |
codebase | ./codebase-cmd.js | src/index.ts:L914-L934 |
review [id] | ./review-cmd.js | src/index.ts:L937-L950 |
ci extract-mr | ./ci/extract-mr.js | src/index.ts:L956-L974 |
deep-enrich hidden | ./deep-enrich.js | src/index.ts:L976-L988 |
recall feedback / maintenance / promote | ./votes.js / ./maintenance/index.js(인라인 로직) | src/index.ts:L990-L1120 |
시작 시 부수효과(bootstrap)
- preAction 자동 마이그레이션(fail-closed). 전역
preAction훅이init/pull/push에 한해./migrate.js의maybeMigrate를 명령 실행 전에 호출해 레거시<repo>/.teamai/를 파티션으로 옮긴다. 훅 서브커맨드(TEAMAI_HOOK_SUBCOMMANDS)는 제외. 실패 시process.exit(1)로 명령을 중단하므로 이 경로는 fail-closed다 src/index.ts:L9-L48. 사용자 확인 없이 파일을 이동한다(--dry-run만 미리보기). - self-heal bootstrap(fail-open, 무확인).
detectProjectConfig()는 git 레포 안에서 파티션 설정이 없으면bootstrapSelfRepo(workspaceRoot, {silent:true})를 호출하고, git 밖에서도readConfigFrom(..., selfHealRepoRoot)경로로 같은 부트스트랩을 돈다 src/config.ts:L273-L307, src/config.ts:L331-L361.autoDetectInit()이 이 감지를 부르므로(src/config.ts:L425-L435) 설정을 읽는 모든 명령과 session-start 훅에서 발동할 수 있다(파일 주석도 "on any teamai command or session-start hook" src/bootstrap.ts:L11-L15, project-stated). - 부트스트랩 조건:
.teamai/teamai.yaml에mode: self이고 로컬 config가 없을 때 src/bootstrap.ts:L77-L82, 그리고 provider가 이미 인증된 상태일 때만 진행(비인증이면 skip, 훅에서 로그인 유발 금지) src/bootstrap.ts:L109-L118. 락 파일.teamai/<BOOTSTRAP_LOCK_FILENAME>으로 동시 실행을 막는다 src/bootstrap.ts:L84-L89. - 부트스트랩이 사용자 확인 없이 수행하는 것: ①
provider.authenticate()로 username 해석 src/bootstrap.ts:L120-L126 ② HOME에 설치된 AI 도구 감지 후 파티션에config.yaml저장 src/bootstrap.ts:L149-L180 ③ 도구 디렉터리 시드 + 훅 주입 src/bootstrap.ts:L191-L206 ④ reports 워크트리에members/<username>.yaml작성 후commitAndPushReports로 원격 push src/bootstrap.ts:L208-L226. ③④ 및 전체는 모두catch → log.debug로 삼켜져'skip'을 반환하므로 fail-open이다 src/bootstrap.ts:L230-L235. - 자동 self-update는 이 파일들에 없다.
update는 명시적 명령이며 src/index.ts:L493-L501,bootstrap.ts가./update.js에서 가져오는 것은 락 유틸(acquireLock/releaseLock)뿐이다 src/bootstrap.ts:L28. 시작 시 npm 레지스트리 조회 등은 검토 범위 파일에서 확인되지 않음(unknown;update.js/hooks.ts미검토). - hook-dispatch는 항상 exit 0(fail-open). 포그라운드 훅 프로세스는 7초 하드 타이머로
process.exit(0)하고,finally에서도 무조건process.exit(0)한다 — 백엔드 fetch가 멈춰도 IDE를 막지 않지만, 동기화 실패도 성공과 같은 종료코드로 끝난다 src/index.ts:L726-L756. 주석은 백그라운드--bg-only자식이 "real syncs/downloads"를 수행한다고 밝힌다(project-stated) src/index.ts:L735-L736. - 읽기 경로의 조용한 config 재작성.
loadLocalConfig*는primaryRole이 없고 매니페스트에hai역할이 있으면 config를primaryRole: 'hai'로 다시 써서 저장한다 src/config.ts:L24-L51.
로컬 설정(config)
- user scope:
~/.teamai/config.yaml, 상태~/.teamai/state.json(주석 및getUserConfigPath/getUserStatePath사용) src/config.ts:L71-L120. 실제 경로 함수 본문은types.ts에 있어 미검토(경로 문자열은 주석 기반 project-stated). - project scope: git 앵커로 키를 만든 파티션
~/.teamai/projects/<slug>/config.yaml이 우선, 없으면 레거시<projectRoot>/.teamai/config.yaml이중 읽기 src/config.ts:L139-L169, src/config.ts:L241-L251. git 밖이면<dir>/.teamaisrc/config.ts:L304-L306. 저장 시 파티션에anchor역참조 파일도 쓴다 src/config.ts:L177-L201. - 저장 필드(생성 코드에서 역산; 스키마 본문은 미검토):
repo{localPath, remote, kind('self'|'http'|없음), businessRepoRoot|url},username,scope,projectRoot,additionalRoles,primaryRole,resourceProfileVersion,projects,enabledAgents,disabledAgents,inheritUserScopesrc/init.ts:L1350-L1382, src/bootstrap.ts:L152-L174.dataHome은 직렬화 시 제거된다 src/config.ts:L88-L97. 상태 파일에는lastPullRev가 있다 src/init.ts:L1422-L1429. - 토큰: config에는 URL만 저장하고 키는 저장하지 않는다는 주석 src/init.ts:L367-L370.
--token은./api-key.js의saveApiKey로 별도 파일(getApiKeyPath())에 저장 src/init.ts:L341-L346; "stored 0600, never committed"은 옵션 설명(project-stated) src/index.ts:L57..teamai/.gitignore에token항목이 있어 토큰 파일이.teamai/에 놓일 수 있음을 시사한다(inference) src/init.ts:L1394-L1411. - username은 provider 인증 결과가 그대로 config에 저장되고
members/<username>.yaml파일명으로 팀 레포에 올라간다 src/init.ts:L1277-L1285. HTTP 모드는 고정값'http-consumer'src/init.ts:L368-L371. machine id는 이 다섯 파일의 config 저장 경로에는 나타나지 않음(src/machine-id.ts는 범위 밖, unknown).
init 흐름 (end-to-end)
init()은 세 갈래로 분기한다: --http → initHttp, --self 또는 repo 인자 . → initSelfRepo, 그 외 → git 팀 레포 모드 src/init.ts:L979-L1002.
- scope: 기본
project(cwd), cwd가 HOME이면user로 폴백;--scope는user|project만 허용 src/init.ts:L169-L211. 데이터 홈은 기존 config의dataHome또는 파티션 src/init.ts:L1036-L1039. (test-present: src/__tests__/init.test.ts:L498-L584) - 재init 가드: config가 있으면
askConfirmation,--force면 무확인 덮어쓰기 src/init.ts:L1046-L1058. - repo 입력: positional/
--repo충돌 시 에러, 둘 다 없으면askQuestion— TTY 여부 검사 없음 src/init.ts:L1061-L1076. - 인증:
provider.ensureInstalled()후isAuthenticated()면authenticate(), 아니면 "starting authentication"으로authenticate()호출. provider 이름이git이면 "Git identity"를 쓴다 src/init.ts:L1098-L1116. 실제 인증 방식(OAuth 여부)은providers/에 있어 이 범위에서 확인 불가. - clone: 대상
<teamaiHome>/team-reposrc/init.ts:L1118-L1120. 기존 clone의 remote가 다르면 제거 확인(또는--force) src/init.ts:L1122-L1165.RepoNotFoundError면 "Create repo? [Y/n]" 기본 Yes로provider.createRepo(원격 레포 생성) 후 재clone src/init.ts:L1167-L1213; 빈 레포면initReposrc/init.ts:L1214-L1226. (test-present: src/__tests__/init.test.ts:L225-L402) - clone 내부 git user 설정
configureGitUser(localPath, username, username, ..., emailDomain)src/init.ts:L1229-L1231. - teamai.yaml 없으면 기본 생성 +
members/skills/rules/docs/env디렉터리(.gitkeep) src/init.ts:L1236-L1260. - 멤버 등록 → 팀 레포 push: clone 안에
members/<username>.yaml을 쓰고, 변경이 있으면--dry-run이 아닌 한pushRepoDirectly로members/,teamai.yaml, 각.gitkeep을 커밋·push한다. push 실패는 warn만 src/init.ts:L1274-L1309. 즉 init은 팀 레포에 push한다. - 리뷰어 프롬프트(신규 멤버·reviewers 없음·
--force아님일 때만): 입력 시teamai.yaml에reviewers기록 후 push src/init.ts:L1311-L1348. - 로컬 config 저장: 역할은
--role/단일 역할 자동/프롬프트 src/init.ts:L64-L115; project scope면saveLocalConfigForScope+.teamai/.gitignore생성(config.yaml, state.json, token, env, sessions/, learnings/등), user scope면~/.teamai/config.yamlsrc/init.ts:L1384-L1419.state.lastPullRev = null로 다음 pull 전체 동기화 src/init.ts:L1421-L1429. - 훅 주입 + built-in skills:
reconcileTeamHooksForConfig(AI 도구 설정 파일 수정; 본문은hooks.ts, 미검토) 후deployBuiltinSkillssrc/init.ts:L1431-L1449. (test-present: src/__tests__/init.test.ts:L458-L496)
single-repo 모드(init ./--self)가 디스크·원격에 쓰는 것: 비즈니스 레포 안 .teamai/{skills,rules,docs,learnings,env,agents,hooks,mcp}/.gitkeep src/init.ts:L772-L783, .teamai/teamai.yaml(mode: self, 원격 URL·provider 포함) src/init.ts:L785-L802, .teamai/.gitignore src/init.ts:L855-L856, 파티션의 config.yaml src/init.ts:L843-L850, 도구 디렉터리 시드·훅 주입 src/init.ts:L858-L876, 그리고 현재 브랜치에 자동 git commit(skeleton + 선택된 도구의 settings 파일; push는 안 함) src/init.ts:L878-L921, teamai-reports orphan 브랜치에 멤버 파일 커밋 후 원격 push src/init.ts:L923-L953. 도구 선택은 --agent > 비TTY/--silent/--force면 HOME 감지 > 대화형 피커 src/init.ts:L612-L664.
HTTP 모드(--http): clone·push 없음. --token을 API 키 파일에 저장, <teamaiHome>/team-repo/teamai.yaml 스텁 생성, config 저장, 훅 주입, initLocalAgentHttp 초기화 src/init.ts:L341-L431. 주석상 리소스는 세션마다 "report/sync/ack" 왕복으로 전달된다(project-stated) src/init.ts:L276-L280.
문서 vs 코드 불일치
| 문서 주장 | 코드 | 판정 |
|---|---|---|
README: teamai init = "OAuth login, link repo, register member, inject hooks" README.md:L239 | 코드는 provider.authenticate()만 호출하고, provider가 git이면 "Git identity" 해석으로 대체된다 src/init.ts:L1100-L1111. OAuth 구현은 providers/(미검토) | 부분 일치 — provider 의존; "OAuth"는 보편 사실이 아님 |
| README: 등록 4가지에 push 언급 없음 | git 모드 init은 members/·teamai.yaml·.gitkeep을 팀 레포에 push src/init.ts:L1290-L1305; 존재하지 않는 레포는 기본 Yes 프롬프트로 원격 생성 src/init.ts:L1176-L1189; self 모드는 현재 브랜치 commit + orphan 브랜치 push src/init.ts:L906-L941; built-in skills 배포도 수행 src/init.ts:L1437-L1448 | 문서가 원격 쓰기 부수효과를 누락 |
| README: "register member"는 init의 단계 | self 모드 레포에서는 init 없이도 임의 명령/훅의 config 감지 경로에서 bootstrapSelfRepo가 멤버 등록 + push를 수행 src/bootstrap.ts:L208-L226, src/config.ts:L292-L297 | 문서가 init 외 경로를 누락(사용자 확인 없음) |
README: "every AI session automatically pulls the latest skills / rules" via SessionStart hook → teamai pull README.md:L53, README.md:L124 | 범위 내에서 보이는 것은 훅 주입 호출과 hook-dispatch 명령뿐이며, 그 명령은 7초 하드 exit·무조건 exit 0이다 src/index.ts:L737-L756. 실제 pull 호출은 hook-dispatch-cli.js/hooks.ts에 있어 미검토 | project-stated; "매 세션 최신 보장"은 코드가 fail-open이라 보장 형태가 아님(inference) |
init 도움말: "configure Git provider, clone repo, register member" src/index.ts:L52 | 훅 주입·AI 도구 설정 파일 수정·built-in skills 배포·(self) 자동 커밋은 도움말에 없음 src/init.ts:L1431-L1448 | 도움말이 부수효과를 축소 서술 |
--http: "read-only consumer; only needs an API key" src/index.ts:L55 | HTTP init도 훅(리포터 포함)을 주입하고 로컬 에이전트를 초기화하며, 주석은 세션마다 report/sync/ack 왕복을 명시 src/init.ts:L414-L430 | "read-only"는 팀 레포 쓰기 기준; 세션 데이터 발신은 별개(inference) |
pull 흐름 (end-to-end)
진입. pull()은 프로젝트 스코프를 먼저 감지하고, 스코프별 sync-lock(<dataHome>/.sync-lock)을 잡은 뒤 pullForScope()를 호출한다. 다른 프로세스가 락을 쥐고 있으면 그 스코프는 조용히 건너뛴다(log.info 한 줄, 실패 아님) src/pull.ts:L1404-L1440. 프로젝트 스코프가 있으면 사용자 스코프는 기본 생략(inheritUserScope 시 skills/rules/docs/agents만 상속) src/pull.ts:L1459-L1480.
팀 repo 취득 방식. 백엔드 3종. git: pullRepo(localPath)(utils/git.js, simple-git 기반으로 추정 — createGit·git.reset API 형태에서 [inference]) 후 getHeadRev로 버전 결정 src/pull.ts:L102-L127. http: 클론 없음, API key 필수, 리소스는 별도 report/sync 핸들러로 전달(reportingOnly=true) src/pull.ts:L67-L76. self(단일 repo): git pull을 실행하지 않고 <repo>/.teamai의 HEAD만 읽는다 src/pull.ts:L78-L99.
pull 안의 숨은 push. git 백엔드에서는 git pull 직후 flushPendingLearnings()가 이전에 실패한 learnings의 add/commit/push를 재시도한다(주석이 명시: "add/commit/push") src/pull.ts:L102-L117. 또한 pull() 말미의 Step 4는 ~/.teamai/usage.jsonl의 스킬 사용 이벤트를 reportUsageToTeam()으로 팀 repo에 보고한 뒤 로컬 파일을 truncate 한다(http 제외, dryRun 제외) src/pull.ts:L1542-L1592. 즉 "pull"이라는 이름의 명령이 매 세션 아웃바운드 git 쓰기를 동반한다(team-push.js 본문은 미고정이므로 push 여부 자체는 파일명·주석 기반 [inference]).
rev fast-path. state.lastPullRev === HEAD이고 설치된 툴 집합이 같으면 리소스 동기화를 건너뛰되, built-in agents/rules/skills와 CLAUDE.md recall 블록은 매번 재배포한다 src/pull.ts:L504-L541. hooks/MCP/co-author reconcile은 fast-path 바깥에서 항상 실행된다 src/pull.ts:L1525-L1541. test-present: pull-skip-sync.test.ts L142-L279(rev 일치/불일치/force/dryRun/getHeadRev 실패), L397-L414(fast-path에서 recall 블록 갱신).
필터. role 네임스페이스는 manifest/roles, project는 manifest/projects.yaml에서 해석해 합집합 src/pull.ts:L130-L239. rules는 namespace/ 접두가 있는 것만 knowledge 네임스페이스로 걸러지고 루트 rule은 항상 포함 src/pull.ts:L250-L262. skills는 (role/project 디렉터리) ∪ (구독 tag 일치) − excludedSkills src/pull.ts:L601-L639. role 미설정·project 매니페스트 없음이면 전량 동기화(null 컨텍스트) src/pull.ts:L150.
| 리소스 | 대상 경로(툴별) | 근거 |
|---|---|---|
| skills | <baseDir>/<toolPaths[tool].skills>/<name> — 디렉터리 단위 flat 설치. 구체값은 teamai.yaml toolPaths(기본값은 미고정 types.ts)이며 문서상 ~/.claude/skills/, .opencode/skills/·~/.config/opencode/skills/, .qoder/skills/, .zcode/skills/ [project-stated] | src/pull.ts:L335-L347, docs/usage-guide.md L273, L1350, L1358, L1362 |
| rules | <baseDir>/<toolPaths[tool].rules>/<name>.md(툴별 .mdc 확장자 변형 포함); 문서상 ~/.claude/rules/; OpenCode는 opencode.json instructions glob 추가 | src/pull.ts:L576-L599, src/pull.ts:L736-L740, usage-guide L274, L1352 |
| agents | <baseDir>/<toolPaths[tool].agents>/<name>.md; 문서상 ~/.claude/agents/, .zcode/agents/; built-in teamai-recall 에이전트도 동반 배포 | src/pull.ts:L713-L721, src/pull.ts:L1016-L1028, usage-guide L1343 |
| docs | docsHandler.pullItem(핸들러 미고정 → 대상 경로 unknown); teamwiki/는 .teamai/team-repo/ 안에 머문다 | src/pull.ts:L661-L673, src/pull.ts:L883 |
| env | <dataHome>/env.sh(getDataHome; 사용자 스코프면 ~/.teamai/env.sh [inference]) | src/pull.ts:L645-L659 |
| learnings | 사용자 스코프: getUserLearningsDir()로 미러; 프로젝트 스코프: repo 직접 사용. 검색 인덱스 <dataHome>/search-index.json | src/pull.ts:L864-L910 |
| culture / claudemd / recall 블록 | <baseDir>/<toolPaths[tool].claudemd>에 마커 섹션으로 주입(injectClaudeMdSection; 문서상 ~/.claude/CLAUDE.md, ~/.cursor/CLAUDE.md); recall 블록은 agents+claudemd 둘 다 있는 Tier-1 툴만 | src/pull.ts:L922-L986, src/pull.ts:L1201-L1236, usage-guide L1041-L1042 |
| hooks | <hookScope.baseDir>/<toolPaths[tool].settings> — 아래 섹션 표 참조 | src/pull.ts:L1623-L1647 |
| mcp | reconcileMcpForConfig(미고정 mcp-reconcile.js); 문서상 ~/.cursor/mcp.json, ~/.codebuddy/mcp.json, ~/.codex/config.toml, .qoder/settings.json, opencode.json [project-stated] | src/pull.ts:L1654-L1680, usage-guide L729-L734 |
| packages | pull은 설치하지 않는다. SessionStart 핸들러가 pull 전후 packageManifestHash를 비교해 힌트만 stash하고, 실제 설치는 별도 teamai packages 명령 | src/hook-handlers.ts:L84-L106, src/index.ts L6, README L242 |
| co-author 정책 | 툴 설정에 attribution 트레일러 on/off를 쓰기 전용으로 반영(정책 철회 시 되돌리지 않음) | src/pull.ts:L1536-L1541 |
삭제 의미론. (1) tombstone: 팀 repo가 tombstone으로 표시한 rules(.md/.mdc)·skills(디렉터리)·agents(.md)를 설치된 모든 툴에서 remove()한다. skills만 .git 등 VCS 메타데이터가 있으면 보존·경고하고, rules/agents 파일은 로컬 수정 여부를 검사하지 않고 삭제한다 src/pull.ts:L708-L759. (2) 비활성 role/project 네임스페이스 skill 및 desired 집합 밖의 skill: 팀 소스와 바이트 동일(dirContentEqual, CONTRIBUTORS 제외)할 때만 삭제, 아니면 보존·경고 src/pull.ts:L294-L363, src/pull.ts:L772-L798. (3) 구버전 네임스페이스 중첩 복사본 중 excludedSkills는 SKILL.md 존재만 확인하고 안전 게이트 없이 삭제 src/pull.ts:L800-L815. (4) pullAllRules는 빈 집합에서도 호출되어 stale rule 파일을 정리한다(핸들러 본문 미고정) src/pull.ts:L588-L592. 개별 pullItem의 덮어쓰기/병합 규칙은 resources/*가 미고정이라 unknown.
push 흐름 (end-to-end)
브랜치·커밋. 브랜치명 = generateBranchName(localConfig.username)(utils/git.js 미고정 → 정확한 포맷 unknown), 커밋 메시지 = [teamai] Push N resource(s) from <username>, 스테이징 대상 = pushItem 경로 + 존재하는 sweeper 디렉터리 rules/, env/, .codebuddy-plugin/ + (편집된 경우) teamai.yaml src/push.ts:L188-L212. 이미 열린 PR이 있는 리소스는 그 브랜치를 force-push로 갱신한다(reuseBranch; 주석 "the force-push above") src/push.ts:L228-L232. test-present: push-pending-pr.test.ts L308-L460.
MR/PR 생성. getProvider(teamConfig.provider)가 반환한 provider의 createPullRequest({repo, source, target=default branch, title, description, reviewers: teamConfig.reviewers, cwd})를 호출한다. 실패해도 브랜치는 이미 원격에 있고, provider가 git인데 GitLab로 감지되면 provider: gitlab·GITLAB_URL·GITLAB_TOKEN 설정을 안내한다 src/push.ts:L86-L139. provider 구현(providers/*)은 미고정이라 gh/glab CLI인지 REST API인지는 unknown; GITLAB_TOKEN with api scope 문구는 API 토큰 사용을 시사한다 [inference].
무엇이 올라가는가. 스캔 대상은 ['skills','rules','env','agents']뿐이며 세션 트랜스크립트·usage 이벤트·learnings는 teamai push 경로에 없다 src/push.ts:L504-L513. 단 env 타입이 pushable이고 env/ 전체가 sweeper로 스테이징되므로 로컬 env 파일에 시크릿이 있으면 PR로 나갈 수 있다(env 핸들러 미고정 → 마스킹 여부 unknown). learnings는 /teamai-share-learnings 스킬과 pull의 flushPendingLearnings, votes는 Stop 훅에서 별도 push된다(아래).
확인 게이트. 대화형 실행은 번호 선택 프롬프트(기본 all)를 거치지만 --all 또는 --silent면 전량 자동 선택 src/push.ts:L738-L753. --dry-run은 표시 후 종료 src/push.ts:L732-L737. 안전장치: 팀 클론이 전용 git root가 아니면 중단(사용자 작업 트리 보호) src/push.ts:L436-L447; self 모드는 격리 worktree에서 실행 src/push.ts:L333-L388; 실패 시 팀 클론에 reset --hard + clean -fd src/push.ts:L258-L273. 리뷰·승인·머지 자체를 강제하는 코드는 push.ts에 없다 — reviewers를 PR 필드로 넘길 뿐이다.
# commands — not run (README L122, L241, L242 인용)
teamai push → create branch + MR → reviewer approves + merges
teamai push # Push local resources to a branch and open a Merge Request
teamai packages [install] [target]
훅 주입과 런타임 핸들러
주입 엔진. 모든 툴은 단일 teamai hook-dispatch 명령으로 통일되어 있고(레거시 개별 서브커맨드는 마이그레이션 시 정리) src/hooks.ts:L24-L28, 포맷은 claude(Claude Code·CodeBuddy·WorkBuddy·Qoder 등 settings.json), cursor(hooks.json, camelCase 이벤트), codex(hooks.json), zcode(~/.zcode/cli/config.json의 hooks.events, hooks.enabled=true를 강제로 켠다) 4종 src/hooks.ts:L128-L139, src/hooks.ts:L545-L553. Hermes는 config.yaml, OpenClaw 계열(openclaw/qclaw/easyclaw/autoclaw)은 HOOK.md+handler.ts, OpenCode는 ~/.config/opencode/plugin/의 플러그인 파일(항상 HOME, 프로젝트 복사본은 삭제) src/hooks.ts:L13-L22, src/hooks.ts:L953-L981, src/hooks.ts:L1067-L1101. 설정 파일 경로 = <baseDir>/<toolPaths[tool].settings>이고 툴 루트 디렉터리가 이미 존재할 때만 쓴다 src/hooks.ts:L1102-L1123. 프로젝트 스코프(self 제외)의 훅도 HOME 설정 파일에 들어가며 팀 훅 명령은 $PWD 게이트로 감싼다 src/hooks.ts:L238-L256, src/pull.ts:L1381-L1386.
기존 사용자 훅 보존. reconcile은 "teamai 관리 엔트리"(description 접두 [teamai]/[teamai:hook:, 또는 명령에 teamai ... 마커, 또는 manifest에 기록된 명령)만 교체하고 나머지는 untouched로 보존한다 src/hooks.ts:L359-L419. 변경이 없으면 파일을 쓰지 않는다(idempotent). test-present: hooks.test.ts L194-L232(사용자 훅 보존), L130-L192(멱등), L235-L275(제거 시 타 훅 보존). 공개 Codex는 사용자가 /hooks에서 신뢰해야 실행될 수 있고 teamai는 [hooks.state]를 건드리지 않는다 src/hooks.ts:L141-L168.
| 이벤트 | 툴 설정 파일(형식별) | 핸들러 → 런타임 동작 | 근거 |
|---|---|---|---|
| SessionStart / sessionStart | claude: <base>/.claude/settings.json(codebuddy·workbuddy·qoder 동형) · cursor: .cursor/hooks.json · codex: ~/.codex/hooks.json · zcode: ~/.zcode/cli/config.json · opencode: session.created 플러그인 | pull(background, 15s: seedProjectAgentRoot → pull({silent:true}) → package 힌트 stash), dashboard-report(로컬 이벤트 append), mr-hint(gitOnly), package-hint, local-agent-sync(HTTP report/sync) | src/hook-handlers.ts:L452-L460, src/hook-handlers.ts:L84-L106 |
| Stop / stop | 동일 | update(background, npm 레지스트리 조회), votes-sync(gitOnly, 트랜스크립트 파싱 → votes/<user>.yaml 팀 repo push), contribute-check(gitOnly, friction 힌트), dashboard-report(background), local-agent-sync(background) | src/hook-handlers.ts:L462-L473 |
| PostToolUse / postToolUse | 동일(matcher *, Skill, TodoWrite 3엔트리) | dashboard-report, track(Skill 호출·Cursor의 SKILL.md Read를 usage.jsonl에 기록), todowrite-hint(recall 힌트, TEAMAI_RECALL_DISABLED=1로 차단), local-agent-sync | src/hook-handlers.ts:L475-L479, src/hook-handlers.ts:L132-L170 |
| UserPromptSubmit / beforeSubmitPrompt | 동일 | pending-hint(gitOnly, Stop stdout 미지원 툴용 stash 전달), package-pending-hint, track-slash(/skill 프롬프트 기록), dashboard-report, local-agent-sync | src/hook-handlers.ts:L481-L486 |
| PreToolUse | built-in 등록 없음. HTTP 소스의 서버 푸시 "agent hook" 화이트리스트에만 포함 | — | src/hooks.ts:L594-L596 |
Stop 훅이 읽는 것. contribute-check는 stdin의 session_id(없으면 pid+cwd 파생), cwd, transcript_path를 contributeCheckForSession()에 넘긴다; friction 점수 계산·"세션당 1회" 로직은 미고정 contribute-check.ts에 있어 이 증거 세트로는 검증 불가 src/hook-handlers.ts:L210-L232. votes-sync는 transcript_path 파일을 parseTranscriptForVotes로 읽어 recalledDocIds/referencedDocIds를 뽑고, 로컬 votes/<username>.yaml을 갱신한 뒤 syncVotesToTeam(git) 또는 self 모드의 commitAndPushReports로 팀 repo에 push한다; 실패는 조용히 삼킨다 src/hook-handlers.ts:L286-L331. recall 후 선언이 없으면 중국어 nudge 메시지를 모델에 되돌려 다음 턴을 강제한다(Cursor는 세션당 1회 캡) src/hook-handlers.ts:L332-L387. TEAMAI_ADOPTION_EVAL_LOG 설정 시 로컬 파일에 A/B 라인을 추가한다(opt-in) src/hook-handlers.ts:L354-L371.
옵트아웃. contribute 힌트: isContributeHintEnabled(localConfig, teamConfig)를 매 실행 시 평가(README의 sharing.contributeHint.enabled/contributeHintEnabled에 대응 [inference — 함수 본문 미고정]); 설정을 읽지 못하면 enabled로 폴백 src/hook-handlers.ts:L193-L208. recall/votes/todowrite: 환경변수 TEAMAI_RECALL_DISABLED=1 src/hook-handlers.ts:L289, src/hook-handlers.ts:L398. 툴 단위: enabledAgents/disabledAgents로 주입 제외 src/hooks.ts:L1203-L1219; 팀 단위 built-in 훅 disable/timeout 오버라이드는 hooks/hooks.yaml의 builtinOverride src/hooks.ts:L190-L193. SessionStart 자동 pull 자체를 끄는 전용 플래그는 이 파일들에 없다(훅 제거 또는 builtinOverride 경유만 [inference]). HTTP 소스면 gitOnly 핸들러가 제거되며, 설정 파싱 실패 시엔 전체 레지스트리가 유지된다(주석이 "Fail-open by design"이라 명시) src/hook-handlers.ts:L490-L518.
자동 동작의 실행 정책(fail-open/closed 판정)
- SessionStart 자동 pull — fail-open, 무확인. 매 세션 background로
pull({silent:true})가 실행되며 확인 프롬프트가 없다 src/hook-handlers.ts:L452-L456. git pull 실패는pullSpin.fail후return(세션 계속) src/pull.ts:L494-L502; 락 경합은 스킵 src/pull.ts:L1434-L1440. 리소스 배포·훅/MCP reconcile·usage 리포트는 각각 try/catch로 삼켜진다(log.debug) — 부분 실패가 사용자에게 보이지 않는 fail-open src/pull.ts:L1589-L1607. - pull 내 아웃바운드 쓰기(learnings flush, usage 리포트) — fail-open, 무확인. 오류는 debug/error 로그로만 남고 pull은 계속된다 src/pull.ts:L111-L117, src/pull.ts:L1578-L1591. 리포트 전 이벤트 개수를 세고 리포트 후 truncate하므로 push 실패 시에도 truncate가 실행되는지는
reportUsageToTeam이 throw하는지에 달려 있다(미고정) [inference]. - Stop votes push — fail-open, 무확인. 팀 repo push 실패는 빈 catch src/hook-handlers.ts:L323-L330, 전체 핸들러도 빈 catch src/hook-handlers.ts:L388-L391.
- Stop update 체크 — fail-open. 매 Stop마다 background로 npm 레지스트리 조회(
doUpdate본문 미고정, 자동 설치 여부 unknown) src/hook-handlers.ts:L465-L469. - contribute 힌트 게이트 — fail-open(enabled 폴백). 설정 로드 실패 시 힌트가 켜진다 src/hook-handlers.ts:L199-L208. HTTP gitOnly 필터도 config null이면 전체 실행(fail-open) src/hook-handlers.ts:L504-L509.
- 훅 재주입 — fail-open. 레거시 마이그레이션·툴별 reconcile 실패는 warn/debug 후 계속 src/pull.ts:L1514-L1523, src/hooks.ts:L1114-L1123.
- 삭제 게이트 — skills는 fail-closed, rules/agents tombstone은 fail-open. skills는 소스 검증 불가 시 보존 src/pull.ts:L318-L324; rules/agents tombstone은 존재하면 삭제 src/pull.ts:L742-L755.
- push — fail-closed. 락 경합·비전용 git root·패키지 선언 오류·PR 생성 실패는 모두
exitCode=1/중단 src/push.ts:L394-L401, src/push.ts:L316-L332, src/push.ts:L240-L243. 단 pull 단계 실패는 warn 후 진행 src/push.ts:L463-L466.
문서 vs 코드 불일치
- "every AI session automatically pulls … no manual sync needed"(README L53) — 부분 지지. SessionStart 훅이 주입된 툴에서만 성립하며, 훅이 없는 툴(문서 자체가 JoyCode를 수동 pull 대상으로 명시, usage-guide L1377), 락 경합으로 스킵된 스코프 src/pull.ts:L1434-L1440, git pull 실패 src/pull.ts:L499-L502, 공개 Codex의 신뢰 게이트 src/hooks.ts:L141-L150는 조용히 동기화를 건너뛴다. 또한 README는 "pull"이 usage 통계·pending learnings·votes를 팀 repo로 push한다는 사실을 이 문장 근처에서 언급하지 않는다 src/pull.ts:L1542-L1592.
- "Stop hook scores it by friction … interrupted, denied a tool call, retried failing tools"(README L155) — 이 증거 세트로는 unknown. 핸들러는
transcript_path를 넘길 뿐이고 src/hook-handlers.ts:L223-L228 점수 함수는 미고정 파일에 있다. 항목별 신호 계산은 project-stated로만 분류. - "Each session is prompted at most once"(README L165) — unknown(
contributeCheckForSession미고정). 반면 recall nudge는 Cursor만 세션당 1회 캡이고 다른 툴은 선언이 나올 때까지 매 Stop 반복될 수 있다고 코드 주석이 명시 src/hook-handlers.ts:L332-L352 — README의 "at most once"가 contribute 힌트에 한정된 진술임을 독자가 구분해야 한다. - "Teams can switch the hint off with sharing.contributeHint.enabled: false"(README L165) — 부분 지지. 게이트 함수는 존재하나 설정 읽기 실패 시 켜짐으로 폴백한다 src/hook-handlers.ts:L193-L208. 키 이름 매핑은 미고정
types.ts의isContributeHintEnabled에 있다. - "push → create branch + MR → reviewer approves + merges"(README L122) — 부분 지지. 브랜치·PR 생성과
reviewers전달은 코드에 있으나 src/push.ts:L109-L118, 승인·머지를 요구하거나 검증하는 코드는 없고 PR 생성 실패 시 브랜치만 남긴 채 수동 PR을 안내한다 src/push.ts:L120-L123. learnings(flushPendingLearnings)와 votes는 이 MR 경로를 거치지 않고 직접 push된다 src/pull.ts:L111-L117, src/hook-handlers.ts:L313-L330(README L165 "pushes a learning document directly to the team repo"와는 일치). - "Packages … npm packages and Claude Code plugins"(README L142, L242) — pull 경로에서 설치 코드 없음 확인; 설치는 명시적
teamai packages명령이며 SessionStart는 힌트만 낸다 src/hook-handlers.ts:L89-L103. README와 충돌하지 않으나 "자동 pull"에 패키지 설치가 포함된다고 오독될 여지가 있다.
아키텍처·실행 흐름
구성 요소 지도 (핀 트리 기준)
패키지는 단일 npm CLI(teamai)다. 소스 479개 .ts 파일 중 src/__tests__ 249개를 빼면 약 230개 모듈이며, 이번 정독은 그중 엔트리·핵심 3흐름·훅·설정·경계 모듈 13개다. 아래 지도의 "정독" 표시가 없는 계층은 트리 인벤토리(경로·크기)로만 안다.
| 계층 | 모듈 (핀 트리) | 역할 | 이번 범위 |
|---|---|---|---|
| 엔트리 | src/index.ts (1,122줄) · src/bootstrap.ts · src/config.ts | commander 프로그램, 76개 .command( 등록, preAction 자동 마이그레이션, self 모드 자동 부트스트랩, 설정 파티션(~/.teamai/projects/<slug>/) | 정독 |
| 핵심 흐름 | src/init.ts · src/pull.ts · src/push.ts | 팀 repo 연결·멤버 등록·훅 주입 / 팀 repo → 툴 디렉터리 미러링 + usage·learnings 역송신 / 로컬 리소스 → 브랜치·PR | 정독 |
| 훅 | src/hooks.ts · src/hook-handlers.ts · src/hook-dispatch*.ts · src/builtin-hooks.ts | 툴별 settings 파일에 teamai hook-dispatch 주입(SessionStart·Stop·PostToolUse·UserPromptSubmit), 런타임 핸들러 레지스트리 | hooks.ts·hook-handlers.ts 정독 / dispatch-cli·builtin 미정독 |
| 툴 어댑터 | src/local-agent.ts (3,429줄) · src/known-agents.ts · src/types.ts (toolPaths) | Claude Code·Codex·Cursor·CodeBuddy·OpenCode·OpenClaw·Hermes·Qoder·ZCode 등 툴별 경로·형식 | 미정독 (경로만) |
| Git 제공자 | src/providers/ 29파일 (github/gitlab/gitcode/cnb/tgit/git) | 인증·clone URL·PR 생성·레포 생성 | 미정독 (docs/providers.md만 정독) |
| 지식 계층 | src/recall.ts · src/wiki-engine/ 31파일 · src/codebase*.ts · src/import*.ts · src/deep-enrich.ts | BM25+그래프 검색, tree-sitter AST 추출, teamwiki 생성, AI CLI로 문서 생성 | 미정독 (README 주장은 11절에서 unknown 처리) |
| 측정·리포트 | src/usage-tracker.ts · src/session-collector.ts · src/dashboard*.ts · src/digest.ts · src/votes.ts · src/utils/reports-branch.ts | 스킬 사용 이벤트, 세션 요약, votes, 팀 stats/·teamai-reports 브랜치 | usage-tracker·session-collector 정독 / 나머지 미정독 |
| 경계 | src/api-key.ts · src/machine-id.ts · src/utils/ai-client.ts | API 키 파일(0600 평문), 기기 식별자, 외부 AI CLI 호출 | 정독 |
실제 엔트리포인트
teamai→dist/index.js (= src/index.ts)→program.hook('preAction') → maybeMigrate→.command().action → 지연 import→detectProjectConfig (self 모드면 bootstrapSelfRepo)빌드 산출물은 tsup ESM 번들 하나이며 package.json의 bin이 유일한 진입이다 package.json:L6-L8. 명령 액션은 실행 시점에 await import('./x.js')로 모듈을 로드하므로 콜드 스타트에 전체 모듈이 올라오지 않는다 src/index.ts:L69-L73. 훅이 호출하는 hidden 명령 8개(track·contribute-check·dashboard-report 등)도 같은 프로그램에 등록돼 있다.
end-to-end 흐름 1 — 세션 시작 (SessionStart 훅 → 자동 pull → 역송신)
teamai hook-dispatch→7초 하드 타이머 · 항상 exit 0→background pull({silent:true})→git pull 팀 repo→flushPendingLearnings (add/commit/push)→skills/rules/docs/agents → 툴 디렉터리→tombstone 삭제 · hooks/MCP reconcile→usage.jsonl → 팀 stats 보고 후 truncate디스패처는 IDE를 막지 않기 위해 7초 뒤 무조건 process.exit(0)한다 src/index.ts:L737-L756; 핸들러는 pull을 silent로 백그라운드 실행하고 실패를 삼킨다 src/hook-handlers.ts:L452-L456. pull 본문은 git pull 직후 미전송 learnings를 push하고 src/pull.ts:L102-L117, 말미에 ~/.teamai/usage.jsonl을 팀 repo로 보고한 뒤 비운다 src/pull.ts:L1542-L1592. 즉 "pull"은 양방향이다.
end-to-end 흐름 2 — 세션 종료 (Stop 훅)
Stop 핸들러 레지스트리는 src/hook-handlers.ts:L450-L488에 있다. votes-sync는 TEAMAI_RECALL_DISABLED=1만 확인하고 push 실패를 삼킨다 src/hook-handlers.ts:L286-L331. friction 점수 함수 자체는 contribute-check.ts(미정독)에 있어 README의 "신호 3종·세션당 1회"는 이 문서에서 검증되지 않았다.
end-to-end 흐름 3 — 공유 (push → PR)
teamai push→팀 클론 reset --hard + pull→로컬 툴 디렉터리에서 skills/rules/env/agents 스캔→번호 선택 프롬프트 (--all/--silent면 생략)→브랜치 generateBranchName(username) · 커밋→provider.createPullRequest(reviewers)→실패 시 브랜치만 남기고 수동 안내스캔 대상은 4종뿐이며 env/는 디렉터리째 스테이징된다 src/push.ts:L504-L513. 승인·머지를 요구하는 코드는 없고, learnings(pull)·votes(Stop)는 PR을 거치지 않고 직접 push된다 src/push.ts:L86-L139.
문서 vs 관측 차이 (요약)
- README "push → MR → pull"의 세 화살표 밖에 init의 팀 repo push·원격 레포 생성(기본 Yes), pull 안의 learnings/usage push, Stop의 votes push가 있다 (4절·9절).
- README "모든 세션이 자동 pull" — 훅이 주입된 툴에서만, 락 경합·git 실패·Codex 신뢰 게이트는 조용히 건너뛴다 src/pull.ts:L1434-L1440 src/hooks.ts:L141-L150.
- README "init: OAuth login" — 제공자 의존; 일반
git제공자는 Git identity 해석만 한다 src/init.ts:L1100-L1111.
설치와 첫 성공
이 절의 명령은 전부 저장소 저자가 쓴 것을 인용한 것이며 이 문서 작성 과정에서 하나도 실행하지 않았다 (commands — not run). 성공 신호와 정리 절차도 문서·코드에서 읽은 것이지 관측이 아니다.
선행 조건
- Node.js —
package.json에engines선언이 없다 package.json; CI 매트릭스는 Node 20/22 × ubuntu/macos ci.yml:L19-L27 (Windows 러너 없음). AGENTS.md는 "Node 20+" AGENTS.md:L5. - git 호스트 저장소(GitHub·GitLab·GitCode·CNB·TGit·사설 git) + 팀원 쓰기 권한 README.md:L36. 제공자 CLI(
gh·gf·cnb)나 토큰이 인증에 쓰인다 docs/providers.md. - 대상 AI 툴이 HOME/프로젝트에 이미 설치돼 있어야 훅·리소스가 쓰인다 — 툴 루트가 없으면 건너뛴다 src/hooks.ts:L1102-L1123.
저자 명령 인용 (commands — not run)
# commands — not run (README.md L30-L32, L45-L50; 이 문서는 실행하지 않았다)
npm install -g teamai-cli
cd /path/to/my-project
teamai init https://github.com/yourorg/yourrepo # project scope (기본)
teamai init https://github.com/yourorg/yourrepo --scope user # user scope (~/)
이 문서의 판단(3절)에 따르면 첫 실행은 전역 옵션 --dry-run과 함께 하는 편이 안전하다 — commander 전역 옵션으로 정의돼 있고 init의 팀 repo push를 막는다 src/index.ts:L13-L23 src/init.ts:L1284-L1305. 안정 버전 고정([email protected])은 이 문서의 권고이지 저자 명령이 아니다.
필요한 환경 · 서비스
| 항목 | 값 | 근거 |
|---|---|---|
| 설정 파일 | ~/.teamai/config.yaml(user) · ~/.teamai/projects/<slug>/config.yaml(project 파티션) | config.ts:L71-L75 config.ts:L139-L169 |
| 팀 클론 | <teamaiHome>/team-repo | init.ts:L1118-L1226 |
| API 키(HTTP 모드) | TEAMAI_API_TOKEN → TEAMAI_API_KEY → ~/.teamai/apikey(평문, 0600) | api-key.ts:L29-L53 |
| 옵트아웃 스위치 | TEAMAI_CONTRIBUTE_HINT_DISABLED=1 · TEAMAI_RECALL_DISABLED=1 · sharing.contributeHint.enabled · TEAMAI_SKIP_AST=1 | hook-handlers.ts:L193-L208 hook-handlers.ts:L289 |
기대 성공 신호 (문서·코드 기준)
- init 후 팀 repo에
members/<username>.yaml커밋이 생기고 init.ts:L1276-L1293, 대상 툴 settings에teamai hook-dispatch항목이 4이벤트로 들어간다 hook-handlers.ts:L450-L488. teamai status가 로컬 vs 팀 repo 차이와 리소스 수를 보고한다 (README L243, project-stated).teamai doctor는 설정 진단 (README L264).
제거 · 정리
# commands — not run (README.md L265)
teamai uninstall # 모든 teamai 리소스와 훅 제거 (사용자 생성물 보존은 project-stated, src/uninstall.ts 956줄 미정독)
팀 repo에 이미 push된 members/·stats/·votes/·learnings와 teamai-reports 브랜치는 uninstall이 지우지 않는다고 봐야 한다 [추정 — uninstall.ts 미정독].
비실행 영수증
이 절을 포함해 문서 전체에서 설치·빌드·테스트·실행은 0건이다. 13절의 정적 전용 영수증 참조.
권장 읽기 순서
사용자
- README.md:L26-L55 — 설치·init·자동 pull 계약 (5분)
- docs/usage-guide.md:L359-L427 — 멤버 온보딩과 Auto-sync, 훅 미지원 툴 예외 (8분)
- docs/usage-guide.md:L498-L546 — 스킬 제외와 push 흐름 (6분)
- docs/usage-guide.md:L766-L835 — 공유 힌트 끄기, recall 켜기·끄기 (8분)
- docs/usage-guide.md:L1533-L1547 — uninstall과
--dry-run(3분)
운영자
- README.md:L100-L145 — 배포 제어 4종과 공유 리소스 표 (5분)
- docs/usage-guide.md:L98-L285 — 관리자 init, 다중 프로젝트, 스코프 선택 (15분)
- docs/usage-guide.md:L700-L753 — MCP 배포와 시크릿이 디스크에 남는 경고 (6분)
- docs/usage-guide.md:L1278-L1328 — 내장 훅 표, Codex trust gate, 팀 훅
autoApply(8분) - docs/usage-guide.md:L1398-L1439 — 자동 업데이트 2단 설정, CI extract-mr (6분)
- docs/designs/data-directory-layout.md:L25-L48 — 두 앵커 모델과
~/.teamai/projects/<slug>/(5분)
기여자
- AGENTS.md:L1-L25 — 저장소 규칙 전문; CLAUDE.md와 동일 내용이라는 진술 (3분)
- .github/CONTRIBUTING.md:L1-L80 — 빌드·테스트 명령; 브랜치명·providers 목록 노후 주의 (5분)
- src/index.ts:L1-L120 — 전역 옵션, 자동 마이그레이션 preAction, init/push/pull 등록 (10분)
- src/index.ts:L717-L757 — hook-dispatch 진입점과 타임아웃 설계 주석 (5분)
- CHANGELOG.md:L1-L100 — 최근 세 릴리즈의 변경 밀도와 파티션 리팩터링 (10분)
- docs/designs/data-directory-layout.md:L1-L283 — P0-P3 설계 노트 전문 (20분)
보안 리뷰어
- docs/usage-guide.md:L1278-L1296 — 어떤 이벤트에 무엇이 걸리는지; Codex는 자동 trust하지 않음 (5분)
- src/index.ts:L717-L757 — 훅 프로세스가
process.exit(0)으로 오류를 삼키는 설계와 분리 자식 (5분) - docs/usage-guide.md:L429-L469 — packages: pull은 안내만, 서드파티 코드 자동 실행 없음이라는 진술 (6분)
- docs/usage-guide.md:L749-L753 — MCP 시크릿
${VAR}해석 결과가 프로젝트 설정 파일에 평문으로 남음 (3분) - docs/usage-guide.md:L1232-L1273 — 수집 항목(정정 키워드 감지, 토큰·비용, requests.jsonl 90일)과
session save --push의 직접 커밋 (8분) - docs/usage-guide.md:L1190 —
--deep-enrich가 로컬 AI CLI를 셸 호출(코드 유출 경로) (2분)
건강도·릴리즈
유지보수 건강도 — 캡처 2026-09-11 04:50–05:00Z, 핀 커밋 34e1cfb
아래 수치는 전부 GitHub REST 응답 캡처(페이지당 10~30건 표본)에서 계산한 값이다. 표본 밖의 전수(全數) 주장은 하지 않으며, 각 숫자에 표본 크기와 시간 창을 붙였다. CI 항목은 역사적 메타데이터이지 이 문서가 로컬에서 실행해 얻은 결과가 아니다.
HEAD 신선도
repository.pushed_at= 2026-09-11T03:40:06Z. 캡처 시각(약 04:55Z) 기준 약 1.2시간(0.05일) 전. 핀 커밋의 committer date는 2026-09-11T03:01:12Z(캡처 약 1.9시간 전)이며, pushed_at이 핀 커밋보다 39분 뒤인 이유는 같은 시각에 릴리즈 봇이v0.24.0-beta.6태그를 push했기 때문으로 읽힌다(releases.json published_at 03:40:40Z와 일치).- 핀 커밋 자체:
fix(deep-enrich): retry missing AI docs after temporary failures (#491), 작성자 jimpablo, 단일 부모(squash 머지 산출물). - 최근 커밋 30건 표본(창 2026-09-09T13:12Z → 2026-09-11T03:01Z, 약 1.6일): 작성자별 jeff-r2026 14 · jimpablo 3 · STiFLeR7 3 · FenjuFu 2 · ousamabenyounes 2 · nateEc 2 · linjinglan 1 · Yi-111-a 1 · yakimoto 1 · Beverly621 1. 이 중 머지 커밋 12건은 전부 jeff-r2026가 만든 것이라, jeff-r2026의 14건 중 자기 코드 커밋은 2건뿐이다. 즉 코드 작성은 10명에 분산되지만 main 반영(머지) 권한은 표본상 1인이다.
- 30건 중 committer가
GitHub(웹 UI/PR 머지)인 커밋 16건 — 나머지 14건은 로컬 커밋이 PR을 거쳐 rebase/squash된 흔적.
릴리즈 vs 태그
releases.json(10건)과 tags.json(10건)은 별개 시계열로 봤다. 표본에서는 두 목록의 이름이 완전히 일치한다(태그 10개 = 릴리즈 10개, 태그만 있고 릴리즈 없는 항목 없음).
| 항목 | 값 | 표본·창 |
|---|---|---|
| 릴리즈 수 | 10 (prerelease 8, stable 2, draft 0) | per_page=10, 2026-09-08T06:58Z → 2026-09-11T03:40Z |
| 케이던스 | 10건 / 2.86일 ≈ 3.5건/일 | 표본 첫 published_at → 마지막 published_at |
| 최신 stable | v0.23.1 (2026-09-09T06:37Z) | 직전 stable v0.23.0 2026-09-08T11:01Z |
| 최신 any | v0.24.0-beta.6 (2026-09-11T03:40Z) | beta.1(09-09T04:02Z)부터 6개 베타가 약 2일 안에 나옴 |
| 발행 주체 | 10건 전부 github-actions[bot] | 사람이 손으로 만든 릴리즈는 표본에 0 |
| CHANGELOG 상단 | 0.23.0(09-08) · 0.22.0(09-02) · 0.21.0(08-27) · 0.20.0(08-20) · 0.19.0(08-06) | 핀 블롭 상위 120줄; minor 버전 간격 약 5~14일. 0.23.1 항목은 상위 120줄에 없음 |
- 버전 충돌 기록: 핀 커밋의
package.jsonversion은0.22.0인데, 최신 stable 릴리즈/태그는v0.23.1, 최신 태그는v0.24.0-beta.6, CHANGELOG 최상단은0.23.0이다. 릴리즈 워크플로가 매니페스트 버전을 소스에 되돌려 쓰지 않는 방식이거나 매니페스트가 stale한 것 중 하나인데, 캡처만으로는 어느 쪽인지 판정 불가 —[추정]. - 해석: 하루 3건 이상의 베타 태그는 "활발함"의 신호인 동시에 "베타 채널은 안정 채널이 아님"의 신호다. 도입 시 stable 태그(
v0.23.1) 또는 명시 SHA로 고정해야 하며, npmlatest/베타 태그 추종은 하루 단위로 동작이 바뀔 수 있다.
핀 커밋의 CI (역사적 메타데이터)
- 워크플로(workflows.json, 2건 반환):
CI(.github/workflows/ci.yml, active) ·Release(.github/workflows/release.yml, active). - 최근 실행 20건 표본(main, 창 2026-09-09T06:29Z → 2026-09-11T03:01Z; 저장소 누적 total_count 363): 전부 워크플로
CI, 이벤트push, status completed. 결론 success 16 · cancelled 4(failure 0). cancelled 4건은 연속 push 시 concurrency 취소로 읽히나 캡처엔 사유 필드가 없다 —[추정].Release워크플로 실행은 이 20건 표본에 포함되지 않았다(브랜치 필터 main/push 표본이라 태그 트리거 실행이 잘렸을 가능성). - 핀 SHA
34e1cfb의 실행: run id 34556837353, name CI, event push, conclusion success, created 2026-09-11T03:01:15Z. - 핀 SHA의 check-runs(check-runs-pinned.json, total 6):
Lint & Test (Node 20 on ubuntu-latest)·Lint & Test (Node 22 on ubuntu-latest)·Lint & Test (Node 20 on macos-latest)·Lint & Test (Node 22 on macos-latest)·Build·E2E (GitHub provider, full surface)— 6/6 completed·success, 마지막 완료 03:05:15Z. - 한계: 위는 GitHub가 기록한 결과의 메타데이터다. 이 문서는 테스트를 로컬에서 재실행하지 않았고, 매트릭스에 Windows 러너가 없다는 점(캡처된 6개 check 이름 기준)도 도입 환경에 따라 공백이다.
이슈·PR 응답 표본
| 지표 | 값 | 표본·규칙 |
|---|---|---|
| 열린 이슈 수(API) | open_issues_count = 21 | GitHub 정의상 PR 포함 수치. 열린 표본 21행 = 순수 이슈 15 + PR 6으로 정확히 일치 |
| 열린 순수 이슈 | 15행 (댓글 0개 = 11/15) | issues-open.json, sort=updated, 창 2026-09-01T14:21Z → 2026-09-11T03:49Z. 작성자 jeff-r2026 12/15(메인테이너 자기 기록형 이슈), 외부 3 |
| 닫힌 순수 이슈 | 13행 (PR행 17 제외) | issues-closed.json 30행, closed_at 창 2026-09-10T02:32Z → 2026-09-11T03:29Z; state_reason completed 12 · not_planned 1 |
| 표본 내 닫힘 비율 | 13 / (15+13) = 46% | 두 표본의 시간 창이 다르므로(열린 표본은 10일, 닫힌 표본은 약 1일) 비율 자체보다 "하루에 13건이 닫혔다"는 처리량이 더 유의미 |
| 닫힌 이슈 소요일 | 중앙값 0.73일 (0.04–112일) | n=13, created_at → closed_at |
| 첫 비저자·비봇 응답 | 중앙값 11.8시간, 범위 0.19–955시간 | n=6(#424 #492 #360 #17 #459 #471, 캡처 측이 고른 유계 표본). 규칙: 댓글 중 user.login ≠ 이슈 작성자이고 user.type ≠ Bot인 첫 댓글까지의 시간. 6건 모두 적격 응답 존재 — "댓글 0" 0건, "응답 없음" 0건, 봇 댓글 0건 |
- 응답 표본 세부(시간): #424 0.19h(jeff-r2026) · #459 2.03h(jeff-r2026) · #471 8.62h(jeff-r2026) · #492 15.0h(jeff-r2026) · #360 262.6h ≈ 10.9일(opsnull) · #17 955.3h ≈ 39.8일(jeff-r2026). 응답자 6건 중 5건이 jeff-r2026 — 응답 채널도 사실상 1인. 최근 이슈(9월)는 반나절 내, 오래된 이슈(5월·8월)는 주 단위였다.
- PR: 열린 6건(draft 0, 가장 오래된 것 2026-09-09T14:39Z) · 닫힌 30건 표본(closed_at 창 2026-09-09T03:10Z → 2026-09-11T03:01Z) 중 머지 24건 = 80%, 미머지 닫힘 6건. 머지 소요(created_at → merged_at) 중앙값 8.88시간, 범위 0.4–20.4시간(n=24). 닫힌 PR 작성자 상위: jeff-r2026 9 · jimpablo 5 · STiFLeR7 3 · nateEc 3. list 엔드포인트라
merged_by는 null이며 누가 머지했는지는 커밋 표본(머지 커밋 12/12 jeff-r2026)으로만 추정한다. - 해석: 외부 PR이 하루 안에 머지되는 속도는 빠르지만, 머지·응답·이슈 작성이 모두 한 계정으로 수렴한다. 그 계정의 부재 시 처리량이 어떻게 되는지는 표본으로 알 수 없다.
기여자·커뮤니티
- contributors.json 30명 반환(per_page=30, 더 있을 수 있음), 응답 내 contributions 합계 702. 상위 5: jeff-r2026 549(78.2%) · m0Nst3r873 89(12.7%) · Eyre921 6(0.9%) · laolaoPlayer 6(0.9%) · Iapa 5(0.7%). 상위 2인이 90.9%. 봇 계정 0.
- 주의: contributions는 기본 브랜치 커밋 수이며, 이 집중도는 버스 팩터가 아니다. squash 머지에서는 머지한 사람이 아니라 PR 작성자가 author로 남으므로 이 수치는 "누가 코드를 썼나"에 가깝고, 조직(Tencent) 차원의 접근 권한·백업 메인테이너 존재 여부는 캡처에 없다.
- 커뮤니티 프로필 health_percentage = 62. 파일 존재(API 기준): README true · LICENSE true · CONTRIBUTING true · PULL_REQUEST_TEMPLATE true · CODE_OF_CONDUCT false · ISSUE_TEMPLATE false. 라이선스는
spdx_id NOASSERTION(GitHub가 표준 SPDX로 인식하지 못한 텍스트) — 라이선스 절에서 별도 판정. - 문서 vs API 불일치: 핀 트리에는
.github/ISSUE_TEMPLATE/bug_report.md와feature_request.md가 있는데 community-profile API는 issue_template=false로 응답한다. 프로필 캐시 지연, 템플릿 frontmatter 미인식, 또는 API가 요구하는 형식 차이 중 어느 것인지 판정 불가 — verdict unknown으로 기록.
관심 신호(참고만)
- stars 3,919 · forks 252 · subscribers(watch) 11 — 캡처 시점 단일 스냅샷. 관심도이지 품질·유지보수 지표가 아니며, 특히 watch 11은 star 대비 매우 낮아 "구경은 많고 추적은 적은" 초기 확산 단계 패턴으로 읽힌다 —
[추정]. - languages.json(bytes): TypeScript 4,748,102 · Python 16,297 · JavaScript 3,438. 사실상 TypeScript 단일 언어(99.6%).
보안·개인정보·공급망
자격증명 경계
정적 분석 대상은 pinned commit 34e1cfb의 redacted 사본이다. 아래 모든 진술은 파일:행을 동반하며, 실행·설치·테스트는 하지 않았다 (런타임 동작은 관찰하지 않음).
- HTTP 팀 리포 / 상태 리포터용 API 키는
TEAMAI_API_TOKEN→TEAMAI_API_KEY(legacy) →~/.teamai/apikey순으로 해석된다 src/api-key.ts:L5-L8, src/api-key.ts:L29-L40. 파일은 평문이며0o600으로 생성·재적용된다 src/api-key.ts:L45-L53. 키를teamai.yaml/로컬 config에 쓰지 않고 페이로드에 넣지 않는다는 것은 주석상 선언이다 (project-stated) src/api-key.ts:L10-L12;init --http는 URL만 저장하고 키는 저장하지 않는다 src/init.ts:L340-L349, src/init.ts:L366-L370.--token옵션 설명도 동일 src/index.ts:L57. - Git provider 토큰(project-stated,
docs/providers.md): GitHub는ghCLI 또는GITHUB_TOKEN/GH_TOKENdocs/providers.md:L82-L91; clone 시x-access-token:[email protected]형태로 URL에 토큰이 들어간다 docs/providers.md:L97 — 이 경우 클론된 팀 리포의.git/config에 토큰이 잔존할 수 있다 [추정; 코드 미확인]. CNB도cnb:$CNB_TOKEN@URL 삽입 docs/providers.md:L153. GitCode는 팀 리포 clone에oauth2:<token>@를 remote URL에 의도적으로 영구 삽입한다고 명시 docs/providers.md:L275-L283, 첫 init 시 PAT를~/.netrc(0600)에 기록 docs/providers.md:L265. 반면 GitLab은http.extraHeader로 주입해 URL에 남기지 않는다고 기술 docs/providers.md:L234. TGit(git.woa.com)은 자동 다운로드한gfCLI가~/.netrc에 토큰 저장 docs/providers.md:L110. GitLab/GitCode MR fetch는 호스트가 설정과 다르면 토큰 발신을 거부한다고 기술 docs/providers.md:L238, docs/providers.md:L278. - 팀 env 확산:
teamai pull은${VAR}를 값으로 치환해 각 AI 도구 config 파일에 평문으로 기록한다(신규 파일 0600) docs/usage-guide.md:L751. single-repo(self) 모드에서는.teamai/env/env.yaml이 main에 커밋되므로 비밀값을 넣지 말라고 경고 docs/usage-guide.md:L338, src/init.ts:L463-L469. - CI e2e는
secrets.TEAMAI_TEST_TOKEN을GITHUB_TOKEN으로 재사용하고 clone URL에 삽입한다 (CI 한정) .github/workflows/ci.yml:L93-L98, .github/workflows/ci.yml:L129-L130.
식별자와 개인정보
- machine_id는 안정적 하드웨어/OS 식별자에서 온다: macOS
IOPlatformUUID(ioreg), WindowsMachineGuid(reg query), Linux/etc/machine-id; 실패 시os.hostname()src/machine-id.ts:L44-L94. 보고용local_agent_id는sha1(agent_type + machine_id + sha1(install_path)[:8])[:16]으로 파생된다 src/machine-id.ts:L107-L110. install_path는 로컬에서만 해시된다고 선언 src/machine-id.ts:L4-L11. 원시 machine_id가 페이로드에 그대로 포함되는지는src/local-agent.ts가 사본에 없어 unknown. SHA-1 16hex는 동일 기기+경로에서 재현되는 안정 식별자이며 솔트가 없다 (static-support). - username은 provider 인증에서 얻어 로컬 config에 저장되고 src/init.ts:L1102-L1110, 팀 리포 clone의 git user.name/email로 설정된다(email = username@provider 기본 도메인; TGit은
@tencent.com) src/init.ts:L1229-L1230, docs/providers.md:L123. init은members/<username>.yaml을 팀 리포에 커밋·푸시하고 src/init.ts:L1276-L1293, self 모드는teamai-reportsorphan 브랜치에 푸시한다 src/init.ts:L925-L944. 이후votes/<username>.yamlsrc/hook-handlers.ts:L305-L321,stats/<user>.yaml,sessions/<user>/<YYYY-MM>.md가 같은 키로 축적된다 docs/usage-guide.md:L308. - 세션 요약 페이로드에는
Project: <cwd>즉 절대 작업 경로가 포함된다 src/session-collector.ts:L154-L160 — 사용자명이 든 홈 경로가 그대로 노출될 수 있다 [추정]. 첫 프롬프트는redactWithEnv후 160자 절단 src/session-collector.ts:L106-L108, 팀 푸시 시 기본 제외(includePromptoff; "redact()는 best-effort"라고 자인) src/session-collector.ts:L132-L139. 스크럽 규칙 자체(src/utils/redact.ts)는 사본에 없어 무엇이 마스킹되지 않는지 unknown; 문서는ghp_…류 시크릿 패턴만 예시한다 docs/usage-guide.md:L1275. - Stop 훅은 호스트가 넘긴
transcript_path를 읽어 contribute 판정과 votes 파싱에 쓴다 src/hook-handlers.ts:L223-L228, src/hook-handlers.ts:L291-L299. 즉 전체 대화 전사본이 로컬에서 파싱되며, 문서는 "카운트만 저장, 프롬프트/전사 텍스트는 저장 안 함"이라고 진술 docs/usage-guide.md:L1234 (project-stated; 파서 코드 미확인).
기기 밖으로 나가는 것 (기본값 표)
훅 레지스트리 src/hook-handlers.ts:L450-L488는 SessionStart/Stop/PostToolUse/UserPromptSubmit 전 이벤트에 핸들러를 등록하며, 등록 자체에는 사용자 토글이 없다. 훅은 teamai init이 HOME의 도구 settings에 주입한다 src/hooks.ts:L822, src/hooks.ts:L988-L1009. self 모드 리포를 clone만 해도 다음 AI 세션에서 자동 init(훅 주입 + reports 브랜치 멤버 등록)된다고 문서화 docs/usage-guide.md:L318. 판정: fail-open = 비활성화하지 않으면 발생, fail-closed = 명시 활성화 필요.
| 데이터 흐름 | 기본 | 목적지 | opt-out 스위치 | 근거 |
|---|---|---|---|---|
스킬 사용 이벤트 (Skill 툴콜, /slash 프롬프트 첫 단어) → JSONL | on (fail-open) | 로컬 ~/.teamai/usage.jsonl, known-skills.json | 없음 (레지스트리에 토글 없음) | src/usage-tracker.ts:L156-L165, src/hook-handlers.ts:L477-L484 |
usage.jsonl 자동 리포트 → 팀 리포 stats/ (pull 때, SessionStart 백그라운드 pull 포함) | on (fail-open; http kind·dryRun만 제외) | 팀 git 리포 또는 teamai-reports 브랜치 (원격 push) | 코드상 스위치 미확인 → unknown | src/pull.ts:L1542-L1591, src/hook-handlers.ts:L456 |
대시보드 이벤트 (툴 시퀀스·프롬프트 턴·개입·토큰) → 로컬 events.jsonl; 집계는 stats/<user>.yaml로 팀 공유 | on (fail-open; 4개 이벤트 전부 등록) | 로컬 ~/.teamai/dashboard/; 집계값은 팀 리포 (project-stated) | 없음 ("dashboard reporting keep running") | src/hook-handlers.ts:L117-L130, docs/usage-guide.md:L1236-L1257, docs/usage-guide.md:L795 |
| Stop 훅 contribute 힌트 (transcript 읽어 friction 판정, 모델 컨텍스트에 리마인더 주입) | on (fail-open; config 읽기 실패 시에도 enabled) | 로컬(모델 컨텍스트). 실제 기여는 /teamai-share-learnings 실행 시 팀 리포 PR | sharing.contributeHint.enabled=false / contributeHintEnabled / TEAMAI_CONTRIBUTE_HINT_DISABLED=1 (git 모드만 등록) | src/hook-handlers.ts:L193-L231, docs/usage-guide.md:L797-L801 |
votes-sync (transcript에서 recall doc-id 파싱 → votes/<user>.yaml 커밋·push) | on at handler (fail-open; env 킬스위치만 검사) | 팀 리포 / teamai-reports 브랜치 | TEAMAI_RECALL_DISABLED=1; recall 팀 기본은 false이므로 실효 데이터는 recall 활성 시에만 [추정] | src/hook-handlers.ts:L286-L330, docs/usage-guide.md:L825-L827 |
| Stop 훅 npm 레지스트리 업데이트 확인 (detached) | on (fail-open) | npm registry (HTTP) | 미확인 (update.ts 사본 없음; CI config에 updatePolicy: auto 필드 존재) | src/hook-handlers.ts:L465-L469, .github/workflows/ci.yml:L136 |
| local-agent HTTP report/sync (HTTP 팀 모드: 세션 시작 시 설치 스킬 상태 보고, 서버 명령 pull) | fail-closed 진입(init --http 필요) → 이후 모든 이벤트에서 자동 | 설정한 HTTP 엔드포인트, Bearer API 키 | 없음 (HTTP 모드 해제 외) | src/hook-handlers.ts:L435-L441, src/hook-handlers.ts:L460-L486, docs/usage-guide.md:L389 |
teamai session save --push (세션 요약: 카운트+툴명+cwd; 첫 프롬프트는 --include-prompt) | off (fail-closed; 로컬 저장은 항상) | 팀 리포 sessions/<user>/ 직접 커밋(no PR) | 플래그 미지정 | src/index.ts:L671-L676, src/session-collector.ts:L142-L165 |
| AI CLI 호출 — 프롬프트(코드베이스 문서 생성·enrich 등 소스 코드 포함)를 argv로 전달 | off (fail-closed; 명시 명령 시) | 가장 먼저 발견된 외부 CLI: claude/claude-internal/tclaude/codex/codex-internal/tcodex/codebuddy/workbuddy/openclaw → 그 CLI 뒤의 LLM 벤더 [추정] | 없음 (우선순위 고정, 선택 옵션 미확인) | src/utils/ai-client.ts:L7-L9, src/utils/ai-client.ts:L22-L28, src/utils/ai-client.ts:L198-L203, src/utils/ai-client.ts:L247 |
A/B 측정 로그 (TEAMAI_ADOPTION_EVAL_LOG) | off (fail-closed) | 로컬 지정 파일 | env 미설정 | src/hook-handlers.ts:L354-L371 |
AI CLI 호출에서 프롬프트가 -p <prompt>/exec <prompt> 명령행 인자로 전달되므로 src/utils/ai-client.ts:L198-L203 동일 호스트의 다른 프로세스가 프로세스 목록으로 프롬프트(코드 조각)를 볼 수 있다 [추정]. 프롬프트 앞 60~80자는 debug 로그에도 남는다 src/utils/ai-client.ts:L246, src/utils/ai-client.ts:L256. 별도 HTTP 텔레메트리 엔드포인트(벤더 수집 서버)로의 발신은 읽은 사본 범위에서 발견되지 않았다; 모든 팀 공유는 git push 또는 사용자가 설정한 HTTP 팀 엔드포인트다 (static-support, 사본 범위 한정).
공급망
- 런타임 의존성 15개 package.json:L48-L64: 대부분 caret 범위,
tree-sitter-wasms 0.1.13·web-tree-sitter 0.25.10만 정확 고정 package.json:L60-L61.simple-git(git 서브프로세스),cross-spawn(AI CLI spawn) 포함.overrides: js-yaml ^3.15.1은 구 major 고정 package.json:L65-L67 (이유 미기재).engines필드 없음; CI 매트릭스는 Node 20/22 .github/workflows/ci.yml:L22-L24. 배포 파일은dist/**/*.js, skills, agents, README, CHANGELOG, LICENSEpackage.json:L9-L16. - WASM 파서: README는 "순수 JS 의존성, 네이티브 툴체인 불필요, 로드 실패 시 휴리스틱 폴백,
TEAMAI_SKIP_AST=1" README.md:L210; pinned tree에.wasm파일 없음(패키지에서 로드; external-confirmed tree). WASM 바이너리 출처/무결성 검증은 unknown. - GitHub SBOM(SPDX 2.3, Dependency Graph 생성): 455 패키지, devDependency(
opencode-ai 1.18.23과 플랫폼 바이너리,vitest,standard-version) 포함 (external-confirmed).licenseConcluded집합에 GPL/LGPL/MPL 없음,CC-BY-3.0·빈값 항목 존재. - CI:
npm ci --ignore-scripts를 모든 job에서 사용 .github/workflows/ci.yml:L36-L37, .github/workflows/release.yml:L29-L30. Actions는 태그 고정(actions/checkout@v4,setup-node@v4,upload-artifact@v4,softprops/action-gh-release@v2) — SHA 고정 아님 .github/workflows/ci.yml:L28-L31, .github/workflows/release.yml:L85.ci.yml에 top-levelpermissions:없음 (토큰 권한은 리포 기본값 → unknown). - 릴리즈:
v*.*.*태그 push로 트리거 .github/workflows/release.yml:L3-L6,id-token: write+ npm@^11.5.1 "trusted publishing" +npm publish에 토큰 시크릿 없음 .github/workflows/release.yml:L12-L14, .github/workflows/release.yml:L26-L27, .github/workflows/release.yml:L81-L82 → OIDC 기반 publish이며 provenance는 자동 첨부될 것으로 [추정] (--provenance플래그는 명시되지 않음). 버전-태그 일치 검사·중복 publish 차단·prerelease dist-tag 분리 존재 .github/workflows/release.yml:L32-L79. 태그 push 권한자 = 퍼블리셔; 태그/브랜치 보호는 401로 unknown. - pinned tree(579 엔트리)에 없는 것:
SECURITY.md,CODEOWNERS,NOTICE,.github/dependabot.yml, CodeQL 워크플로 (external-confirmed tree). 있는 것:.github/CONTRIBUTING.md,ISSUE_TEMPLATE/bug_report.md·feature_request.md,PULL_REQUEST_TEMPLATE.md,workflows/ci.yml·release.yml, 루트.coding-ci.yaml(Tencent CODING CI 추정, 내용 미확보). 워크플로 2개 모두 active (workflows.json). GitHub community profile은 health 62%, code_of_conduct false, issue_template false로 보고 — tree에는 템플릿 파일이 있어 Tensions(탐지 규칙 차이 [추정]). - pinned commit은 GitHub 서명 검증
verified: true(external-confirmed, commit-pinned.json). 공개 advisory 0건([])은 "취약점 없음"이 아니라 "리포 advisory DB에 게시된 것 없음"이다.
미확인(unknown) 항목
- Branch protection, Code scanning, Dependabot alerts, Secret scanning — 모두 HTTP 401(미인증) → unknown, 0이 아님.
repository.json의security_and_analysis: null도 동일. - local-agent HTTP 페이로드의 정확한 필드(원시 machine_id 포함 여부, hostname, 설치 스킬 목록) —
src/local-agent.ts사본 없음. redactWithEnv가 마스킹하는 패턴 목록과 미마스킹 범위 —src/utils/redact.ts사본 없음.- usage.jsonl 자동 리포트(
reportUsageToTeam)의 팀측 파일 스키마와 opt-out 존재 여부 —src/team-push.ts사본 없음. - Stop 훅 npm 업데이트 확인의 비활성 경로(
updatePolicy의미) —src/update.ts사본 없음. transcript-parser.ts가 전사본에서 추출하는 필드의 전체 목록 (문서는 "카운트만"이라 진술)..coding-ci.yaml내용(내부 CI가 무엇을 실행·발행하는지),.gitignore내용(~/.teamai/apikey가 "project-scope .gitignore로 커버"된다는 주석의 실제 대상).- npm 패키지에 실제 provenance attestation이 첨부되는지 (레지스트리 조회 미수행).
- GitHub provider clone URL의 토큰이
.git/config에 남는지 (providers.md L97은 URL 삽입을 기술; 코드 미확인). - assets/*.svg 및
skills/·agents/배포 콘텐츠의 별도 라이선스/상표 정책.
라이선스·채택 조건
라이선스: 탐지값 vs pinned 텍스트
| 소스 | 값 | 종류 |
|---|---|---|
pinned LICENSE 본문 | Tencent 헤더(L1-5: "Tencent is pleased to support…", "Copyright (C) 2026 Tencent", "licensed under MIT… does not impose any additional restrictions") + "Terms of the MIT License:" 이하 표준 MIT 허가·고지·면책 조항(L9-13) LICENSE:L1-L13 | static-support |
package.json | "license": "MIT" package.json:L47 | project-stated |
| README 배지 | "License: MIT" → LICENSE 링크 README.md:L12 | project-stated |
| GitHub 라이선스 탐지 (license.json, repository.json) | key: "other", spdx_id: "NOASSERTION" | external-confirmed |
결론(분류): 실질 조건은 MIT와 동일한 permissive 라이선스이며, 헤더가 "MIT 외 추가 제한을 두지 않는다"고 명시한다 LICENSE:L5. 탐지 결과 NOASSERTION과의 충돌은 Tensions로 기록한다: GitHub의 licensee는 본문이 표준 MIT 템플릿과 근사 일치할 때만 SPDX를 부여하는데, Tencent 서문 3문단과 "Terms of the MIT License:" 구분선이 추가되어 유사도 임계값을 넘지 못한 것으로 보인다 [추정]. 조건 자체는 표준 MIT 문구(L9-13)와 문언상 일치하므로 SPDX 표기는 MIT로 취급하되, 자동 도구가 "other"로 보고하는 점을 컴플라이언스 스캐너 설정에서 수동 승인해야 한다.
이 도서(dossier)의 의무
- MIT 고지 의무: "위 저작권 고지와 이 허가 고지(다음 문단 포함)를 복제본 또는 실질적 부분에 포함" LICENSE:L11. 본 도서는 짧은 코드 인용과 permalink를 쓰므로 "실질적 부분" 재배포에 해당하지 않을 가능성이 높다 [추정]; 그럼에도 안전하게 출처(Tencent/teamai-cli, commit 34e1cfb, MIT)와 저작권 표시("Copyright (C) 2026 Tencent")를 도서 하단에 병기한다.
- 코드를 실질적으로 포함·수정·재배포하는 파생물을 만들 경우
LICENSE전문(헤더 포함)을 동봉한다. - 보증 없음(AS IS) 조항 LICENSE:L13 — 채택 판단의 책임은 채택자에게 있다.
NOTICE · assets · vendor
- pinned tree(579 엔트리)에
NOTICE,SECURITY.md,CODEOWNERS파일 없음 (external-confirmed, tree-recursive.json). MIT는 NOTICE를 요구하지 않으므로 결함은 아니다. assets/teamai-cli-icon.svg,teamai-cli-logo.svg,teamai-cli-mark.svg3개 — 별도 라이선스 문구 없음. 리포 LICENSE가 기본 적용된다고 볼 수 있으나 [추정], 상표("Tencent", "TeamAI" 로고)는 MIT가 부여하지 않으므로 도서에 로고를 재사용할 때는 인용 수준으로 제한한다.- npm 배포물에
skills/·agents/콘텐츠가 포함된다 package.json:L9-L16; 별도 라이선스 표기 없음 → 리포 LICENSE 적용 [추정]. - 의존성(SBOM 455개, devDeps 포함):
licenseConcluded집합에 MIT/ISC/Apache-2.0/BSD-2/3/BlueOak-1.0.0/CC0/Unlicense/CC-BY-3.0이 있고 GPL·LGPL·MPL은 없다 (external-confirmed, GitHub Dependency Graph 기준). 빈값 항목이 있어 전수 확인은 아니다.
unknown
- GitHub 탐지가 실패한 정확한 원인 (licensee 유사도 수치) — 위 설명은 추정.
- assets SVG의 원저작자/디자인 라이선스, Tencent 상표 사용 정책.
- SBOM에서
licenseConcluded가 빈값인 패키지들의 실제 라이선스. - npm에 게시된 tarball의 LICENSE가 pinned 텍스트와 동일한지 (레지스트리 미조회).
이 섹션은 정적 파일 비교에 기반한 기술적 분류이며 법률 자문이 아니다.
주장 원장
README(핀 커밋 34e1cfb)의 실질적 주장 30건을 src/index.ts 명령 등록·docs/usage-guide.md·CHANGELOG.md·package.json·LICENSE·트리 인벤토리와 대조했다. 정적 검사만 수행했으며(실행·설치·빌드 없음), 구현 모듈이 검사 집합에 없는 주장은 unknown으로 둔다. 증거 종류: project-stated(저장소 문서의 자기 진술), static-support(코드·매니페스트·트리에서 확인), external-confirmed(외부 관측치), inference(추론).
| 주장 ID | 주장 | 증거 종류 | 출처 A | 출처 B | 판정 | 신뢰 | 충돌/열린 질문 |
|---|---|---|---|---|---|---|---|
| C001 | Claude Code·Codex·CodeBuddy·WorkBuddy·OpenCode·Cursor 등 여러 에이전트에 걸쳐 skills/rules/MCP/knowledge를 관리 | project-stated static-support | README.md:L14 | src/index.ts:L18-L21, src/index.ts:L67 | partially-supported | medium | 에이전트별 동기화 writer 모듈은 검사 범위 밖 — 툴별 지원 깊이 미검증 |
| C002 | 설치는 npm install -g teamai-cli | project-stated static-support | README.md:L30-L32 | package.json:L2-L6, docs/usage-guide.md:L88-L94 | supported | medium | npm 레지스트리의 실제 게시 버전·dist-tag 미조회. 전제조건 Node ≥ 20, Git(TGit은 gf, CNB는 cnb CLI)은 README에 없음 |
| C003 | init <repo>는 project 스코프 기본, --scope user로 홈 설치 | project-stated static-support | README.md:L45-L50 | src/index.ts:L51-L58, CHANGELOG.md:L117-L119 | supported | high | — |
| C004 | init 이후 모든 AI 세션 시작 시 자동 pull(수동 동기화 불필요) | project-stated static-support | README.md:L53, docs/usage-guide.md:L414-L416 | src/index.ts:L88-L98, src/index.ts:L717-L757 | partially-supported | medium | 충돌: README '모든 세션' vs usage-guide L416 — 훅 미지원 툴(JoyCode, Gemini CLI)은 수동 pull. 훅 본체(src/hooks.ts, src/hook-handlers.ts)는 다른 분석자 담당 |
| C005 | 11개 에이전트 × 13개 기능 축 지원 매트릭스 | project-stated static-support | README.md:L69-L96 | docs/usage-guide.md:L1356-L1377, CHANGELOG.md:L27 | unknown | low | 충돌: JoyCode는 usage-guide·CHANGELOG 0.23.0에 지원 대상이나 README 매트릭스에 행 없음. 143개 셀의 진위는 에이전트별 writer를 읽어야 판정 가능 |
| C006 | Git 제공자 GitHub · GitLab · GitCode · CNB · TGit · 사설 Git | project-stated static-support | README.md:L98, docs/usage-guide.md:L102 | 트리 인벤토리 src/providers/{cnb,git,gitcode,github,gitlab,tgit}/ + src/providers/registry.ts (경로만 확인) | supported | high | 충돌: CONTRIBUTING.md:L33-L36는 providers/github, tgit 두 개만 나열(문서 노후) |
| C007 | 배포 제어 4종 projects/roles/tags/source | project-stated static-support | README.md:L104-L109 | src/index.ts:L226-L294, src/index.ts:L298-L489 | supported | high | pull 시 필터 적용 로직(src/pull.ts)은 다른 분석자 담당 |
| C008 | learnings 격리: 루트 learnings/ 공유, learnings/<project-id>/ 프로젝트 전용 | project-stated | README.md:L111 | docs/usage-guide.md:L196-L204, CHANGELOG.md:L9 | unknown | low | CHANGELOG상 [Unreleased] 기능 — 어느 릴리즈에 포함되는지 불명(C026). 구현 경로 src/recall.ts·src/pull.ts 미검사 |
| C009 | push → 브랜치+MR → 승인·머지 → SessionStart pull → 로컬 동기화 | project-stated static-support | README.md:L121-L125 | src/index.ts:L75-L98, CHANGELOG.md:L64 | partially-supported | medium | 충돌: usage-guide:L333-L336 docs/hooks/mcp는 push가 아닌 직접 git commit; L1273 session save --push는 PR 없이 직접 커밋. MR 생성 본체는 다른 분석자 |
| C010 | 공유 리소스 11종(Skills…Models) 경로 표 | project-stated static-support | README.md:L131-L143 | src/index.ts:L111-L112, src/index.ts:L205-L206, docs/usage-guide.md:L1107-L1147 | partially-supported | medium | 충돌: README Models '—' vs usage-guide의 ClawPro 모델 배포; remove는 실제 <type> <names...>이고 유형이 skills|rules|agents|mcp로 제한(docs/env/hooks 제거 불가) |
| C011 | Stop 훅이 friction(중단·정정·툴 거부·재시도)으로 점수화 후 /teamai-share-learnings 제안 | project-stated static-support | README.md:L155-L163, docs/usage-guide.md:L772-L784 | src/index.ts:L775-L785, docs/usage-guide.md:L1280-L1286 | partially-supported | medium | 충돌: README·usage-guide L772는 'Stop 훅이 점수화'; contribute-check 설명(L777)과 내장 훅 표(L1284)는 기여 감지를 PostToolUse에 귀속. 점수 산식·임계치 코드 미검사 |
| C012 | 힌트는 세션당 최대 1회, sharing.contributeHint.enabled/contributeHintEnabled로 opt-out(Stop 훅 나머지 유지) | project-stated | README.md:L165 | docs/usage-guide.md:L795-L803, CHANGELOG.md:L29 | unknown | low | dedup·opt-out 판독 코드(src/contribute-check.ts 미검사, src/config.ts·src/hook-handlers.ts는 다른 분석자) 본 분석자 미확인 |
| C013 | /teamai-share-learnings 스킬이 learning 문서를 팀 저장소에 직접 push | project-stated static-support | README.md:L165 | 트리 skills/teamai-share-learnings/SKILL.md(내용 미검사), src/index.ts:L787-L798 | partially-supported | medium | '직접 push'가 기본 브랜치 커밋인지 MR인지 src/contribute.ts 미검사 |
| C014 | Recall 기본 꺼짐, sharing.recall.enabled + 로컬 override, recall enable/disable/status | project-stated static-support | README.md:L169-L175 | src/index.ts:L814-L839, docs/usage-guide.md:L820-L835 | partially-supported | medium | README 미기재 긴급 스위치 TEAMAI_RECALL_DISABLED=1(usage-guide L827). 기본 false 강제는 src/recall-toggle.ts·src/config.ts 확인 필요 |
| C015 | pull이 teamai-recall 서브에이전트를 agents/에 배포, recall --check 사전검사 | project-stated static-support | README.md:L177 | src/index.ts:L802-L806, 트리 agents/teamai-recall.md, docs/usage-guide.md:L1343 | partially-supported | medium | 배포 본체는 src/pull.ts(다른 분석자). hidden todowrite-hint(L841-L852)가 TodoWrite 때마다 recall 상기 |
| C016 | recall <query>는 BM25 + graph-boost 검색 | project-stated static-support | README.md:L180-L187, L245 | src/index.ts:L802-L812, docs/usage-guide.md:L1194 | partially-supported | low | 알고리즘 구현은 src/recall.ts(인벤토리만) 미검사 |
| C017 | import --from-repo/--from-org, codebase --extract/--deep-enrich/--reconcile/--lint로 teamwiki/ 그래프 구축 | project-stated static-support | README.md:L193-L199 | src/index.ts:L854-L899, src/index.ts:L914-L934 | supported | high | README 예시의 --project/--output은 hidden 옵션(L919, L929). --deep-enrich가 로컬 AI CLI를 셸 호출(usage-guide:L1190)은 README 미기재 |
| C018 | WASM tree-sitter AST 트랙 + 휴리스틱 트랙, AST_UNAVAILABLE 폴백, TEAMAI_SKIP_AST=1 | project-stated static-support | README.md:L205-L210 | docs/usage-guide.md:L1196, CHANGELOG.md:L57, 트리 src/wiki-engine/ 36항목 | unknown | low | 트리에 'wasm' 경로 0건(문법은 npm 의존성으로 [추정]). 판독 코드(src/codebase-extract.ts, src/wiki-engine/*) 미검사 |
| C019 | codebase 페이지 recall 결과에 Sources: 줄 포함 | project-stated | README.md:L202-L203 | — | unknown | low | usage-guide에 대응 문장 없음; src/recall.ts 미검사 |
| C020 | recall maintenance --prune --dry-run / --prune --archive / --update-quality | project-stated static-support | README.md:L221-L223 | src/index.ts:L1000-L1076, docs/usage-guide.md:L837-L861 | supported | high | README 미기재 --confidence-writeback·--threshold(기본 0.15). --update-quality는 AI 초안을 .draft.md로 기록 |
| C021 | recall promote [learningId]로 skills/rules/docs 승격 | project-stated static-support | README.md:L247 | src/index.ts:L1078-L1120, docs/usage-guide.md:L868-L881 | supported | high | 승격 기준(confidence ≥ 0.90, upvote ≥ 5, 기여자 ≥ 2, ≥ 14일)은 문서 진술 — src/maintenance/promote.ts 미검사 |
| C022 | digest / session save / dashboard(웹 UI) 명령 존재 | project-stated static-support | README.md:L228-L233 | src/index.ts:L666-L702, docs/usage-guide.md:L1259-L1273 | supported | high | dashboard 기본 포트 3721. session save --push는 PR 없이 sessions/<user>/<YYYY-MM>.md에 직접 커밋; --include-prompt로 redacted 첫 프롬프트 포함 가능(기본 off) |
| C023 | digest 7일 지표(성공·프롬프트·활동시간·추정 비용·캐시·정정) + KB Health 페이지 구성 | project-stated static-support | README.md:L230-L233 | docs/usage-guide.md:L895-L917, L1232-L1255, 트리 src/dashboard*.ts, src/digest.ts | unknown | low | 비용은 Claude 모델 ID 공개 단가 기반 추정치(청구서 아님, usage-guide L1255). 지표 산출 코드 미검사 |
| C024 | Commands 표 27개 항목 전부 CLI에 등록 | static-support project-stated | README.md:L237-L265 | src/index.ts:L51-L1120 (init L51, push L75, pull L88, status L100, skill exclude L149-L183, members L185, remove L205, packages L213, doctor L215, roles L226, projects L298, tags L337, source L418, uninstall L503, session save L670, digest L684, contribute L787, recall L802-L839, import L854, codebase L914, ci extract-mr L956, maintenance L1000, promote L1078) | supported | high | 충돌: README L239 init 'OAuth login'은 index.ts L52 설명에 없음. 표 미기재 가시 명령 11종(list, skill show, update, env, hooks, mcp, stats, hook-dispatch, bind-project, review, recall feedback) + hidden 8종(track, track-slash, dashboard-report, contribute-check, todowrite-hint, mr-hint, deep-enrich, source reconcile-plugins) |
| C025 | 라이선스 MIT | project-stated static-support | README.md:L267-L269 | LICENSE:L1-L13, package.json:L47 | supported | high | LICENSE는 Tencent 서문("추가 제한 없음") + MIT 본문. 의존성 라이선스 미검사 |
| C026 | CLI 버전 = package.json version; 이 커밋의 버전 식별은 일관돼야 함 | static-support project-stated external-confirmed inference | src/index.ts:L13-L21, package.json:L3 (0.22.0) | CHANGELOG.md:L5-L22 ([Unreleased] → 0.23.0 2026-09-08), release.yml:L32-L37 (버전=태그 게이트) | contradicted | high | 충돌: package.json 0.22.0 vs CHANGELOG 0.23.0 릴리즈 기록 vs GitHub Releases 안정 v0.23.1 / 프리 v0.24.0-beta.6(도시에 컨텍스트 제공 관측치, 재조회 안 함). CHANGELOG에 0.23.1 항목 없음. release.yml 게이트상 태그 커밋은 package.json이 해당 버전인 별도 커밋이어야 함 — 핀 커밋의 package.json이 뒤처진 상태 [추정] |
| C027 | AGENTS.md(=CLAUDE.md) 규칙: 영어 출력·중국어 금지·Worktree 필수·새 명령 자제·이중언어 문서 동기화·PR 전 실제 e2e | project-stated static-support | AGENTS.md:L9-L24 | .github/CONTRIBUTING.md:L46-L48, 트리 CLAUDE.md(byte-identical은 도시에 컨텍스트 진술; 로컬 blob 부재로 cmp 미수행) | not-applicable | high | 충돌: AGENTS.md L9 기본 브랜치 main vs CONTRIBUTING.md L46 'branch from master'; AGENTS.md L21 실제 CLI e2e 필수 vs CONTRIBUTING.md L48 vitest+tsc만; AGENTS.md L15 중국어 금지인데 src/index.ts:L954 주석에 중국어(주석은 규칙 대상 밖일 수 있음 [추정]) |
| C028 | teamai-hub 조직 템플릿에서 시작 가능 | project-stated | README.md:L38 | — | unknown | low | 외부 조직 존재·내용 미조회 |
| C029 | Packages는 npm 패키지 + Claude Code 플러그인만, packages [install] [target] | project-stated static-support | README.md:L142, L242 | src/index.ts:L213, 트리 src/pkg/adapters/{npm,claude-plugin}.ts, docs/usage-guide.md:L429-L469 | partially-supported | medium | 'pull이 서드파티 코드를 자동 실행하지 않음'(usage-guide L461)은 훅/pull 코드로 확인 필요(다른 분석자) |
| C030 | uninstall이 모든 teamai 리소스·훅 제거, 사용자 생성물 보존 | project-stated static-support | README.md:L265 | src/index.ts:L503-L512, docs/usage-guide.md:L1533-L1547 | partially-supported | medium | '사용자 생성물 보존' 판별 기준은 src/uninstall.ts 미검사. --dry-run 미리보기·--agent 단일 툴 제거 지원 |
집계: supported 10 · partially-supported 11 · contradicted 1 · unknown 7 · not-applicable 1 (총 30).
README 링크·미디어 처리
저장소 루트 입력이므로 README 완역은 산출물이 아니다(소스 코드가 SSOT, README는 주장). README는 11절 주장 원장의 입력으로만 썼다. 아래는 핀 README(README.md @ 34e1cfb35181, blob 7c0815bd)에 저자가 직접 쓴 링크·이미지 24건의 처리 결과다 — GitHub 크롬·내비게이션은 제외했고, 같은 URL의 반복은 각각 별도 항목이다. 이미지는 라이선스·상표·핫링크 근거가 확립되지 않아 재현하지 않았고(원격 요청 0), 링크는 안전한 절대 URL만 원래 의미 위치의 앵커로 보존했다.
- 01[이미지 생략: teamai-cli]
README.md L2 · document startimage · omitted - 02English
README.md L7 · TeamAI — Make Every Team AI Nativelink · preserved - 03简体中文
README.md L7 · TeamAI — Make Every Team AI Nativelink · preserved - 04CI
README.md L9 · TeamAI — Make Every Team AI Nativelink · preserved - 05[이미지 생략: CI]
README.md L9 · TeamAI — Make Every Team AI Nativeimage · omitted - 06npm version
README.md L10 · TeamAI — Make Every Team AI Nativelink · preserved - 07[이미지 생략: npm version]
README.md L10 · TeamAI — Make Every Team AI Nativeimage · omitted - 08npm downloads
README.md L11 · TeamAI — Make Every Team AI Nativelink · preserved - 09[이미지 생략: npm downloads]
README.md L11 · TeamAI — Make Every Team AI Nativeimage · omitted - 10License: MIT
README.md L12 · TeamAI — Make Every Team AI Nativelink · preserved - 11[이미지 생략: License: MIT]
README.md L12 · TeamAI — Make Every Team AI Nativeimage · omitted - 12Contributors
README.md L20 · Contributorslink · preserved - 13[이미지 생략: Contributors]
README.md L21 · Contributorsimage · omitted - 14contrib.rocks
README.md L24 · Contributorslink · preserved - 15https://github.com/yourorg/yourrepo` — 링크 차단(원문 href 형식 오류)
README.md L36 · Team admin / solo userlink · blocked - 16teamai-hub
README.md L38 · Team admin / solo userlink · preserved - 17docs/usage-guide.md
README.md L55 · Team memberslink · preserved - 18中文版
README.md L55 · Team memberslink · preserved - 19usage guide
README.md L111 · Distribution Controlslink · preserved - 20Usage Guide
README.md L145 · What Gets Sharedlink · preserved - 21tree-sitter
README.md L207 · Codebase Knowledge Graphlink · preserved - 22usage guide
README.md L259 · Commandslink · preserved - 23MIT
README.md L269 · Licenselink · preserved - 24CONTRIBUTING.md
README.md L273 · Contributinglink · preserved
수집 범위·출처·실패
34e1cfb35181241902b7058cedf98dedc5037dee · e3da6c2931d5e9d823c836928455cde0e7e82ed3/versions 응답 [2026-03-10, 2022-11-28]구조화 소스 (endpoint · 상태 · 바이트 · sha256 앞 12자)
| 이름 | endpoint | status | bytes | sha256 | observed |
|---|---|---|---|---|---|
| repository | /repos/Tencent/teamai-cli | 200 | 6,138 | 35678fe7d03c | 04:49:48Z |
| ref-start | /repos/Tencent/teamai-cli/git/ref/heads/main | 200 | 334 | 32124af88bc3 | 04:49:48Z |
| commit-pinned | /repos/Tencent/teamai-cli/commits/34e1cfb35181241902b7058cedf98dedc5037dee | 200 | 30,357 | b381450cf9b6 | 04:49:49Z |
| tree-recursive | /repos/Tencent/teamai-cli/git/trees/e3da6c2931d5e9d823c836928455cde0e7e82ed3?recursive=1 | 200 | 138,676 | f8c00a5007a8 | 04:49:49Z |
| license | /repos/Tencent/teamai-cli/license | 200 | 2,815 | 8ecafe9023e7 | 04:49:49Z |
| community-profile | /repos/Tencent/teamai-cli/community/profile | 200 | 1,030 | dbdeea7272e2 | 04:49:49Z |
| releases | /repos/Tencent/teamai-cli/releases?per_page=10 | 200 | 76,479 | b1c5d768ee2b | 04:49:50Z |
| tags | /repos/Tencent/teamai-cli/tags?per_page=10 | 200 | 4,391 | ec06035123c5 | 04:49:50Z |
| commits-recent | /repos/Tencent/teamai-cli/commits?sha=main&per_page=30 | 200 | 142,475 | 7b9f8a29ee4b | 04:49:50Z |
| workflows | /repos/Tencent/teamai-cli/actions/workflows | 200 | 923 | a29dfa288c48 | 04:49:51Z |
| workflow-runs | /repos/Tencent/teamai-cli/actions/runs?branch=main&per_page=20 | 200 | 245,745 | ebf0eb13b5b4 | 04:49:51Z |
| check-runs-pinned | /repos/Tencent/teamai-cli/commits/34e1cfb35181241902b7058cedf98dedc5037dee/check-runs | 200 | 18,474 | 1ce6d5bc5a2b | 04:49:52Z |
| issues-open | /repos/Tencent/teamai-cli/issues?state=open&per_page=30&sort=updated&direction=desc | 200 | 204,535 | 8184372e15a0 | 04:49:52Z |
| issues-closed | /repos/Tencent/teamai-cli/issues?state=closed&per_page=30&sort=updated&direction=desc | 200 | 190,353 | 576e2bddb339 | 04:49:53Z |
| pulls-open | /repos/Tencent/teamai-cli/pulls?state=open&per_page=30&sort=updated&direction=desc | 200 | 114,459 | 02d03dd0992a | 04:49:53Z |
| pulls-closed | /repos/Tencent/teamai-cli/pulls?state=closed&per_page=30&sort=updated&direction=desc | 200 | 551,225 | b5216e981551 | 04:49:54Z |
| contributors | /repos/Tencent/teamai-cli/contributors?per_page=30 | 200 | 28,488 | e095b411ff07 | 04:49:54Z |
| languages | /repos/Tencent/teamai-cli/languages | 200 | 55 | 495c2d04337f | 04:49:54Z |
| security-advisories | /repos/Tencent/teamai-cli/security-advisories | 200 | 2 | 4f53cda18c2b | 04:49:54Z |
| sbom | /repos/Tencent/teamai-cli/dependency-graph/sbom | 200 | 280,776 | 5946d1a6d082 | 04:49:55Z |
| branch-protection | /repos/Tencent/teamai-cli/branches/main/protection | 401 | 120 | b7dbd173f33b | 04:49:56Z |
| code-scanning-alerts | /repos/Tencent/teamai-cli/code-scanning/alerts | 401 | 120 | b7dbd173f33b | 04:49:56Z |
| dependabot-alerts | /repos/Tencent/teamai-cli/dependabot/alerts | 401 | 120 | b7dbd173f33b | 04:49:56Z |
| secret-scanning-alerts | /repos/Tencent/teamai-cli/secret-scanning/alerts | 401 | 120 | b7dbd173f33b | 04:49:56Z |
| topics | /repos/Tencent/teamai-cli/topics | 200 | 12 | 3913d8d7fcb1 | 04:49:57Z |
| api-versions | /versions | 200 | 27 | d5964be7d7cc | 04:50:17Z |
| issue-424-comments | /repos/Tencent/teamai-cli/issues/424/comments?per_page=30 | 200 | 7,083 | c45c5e46915b | 04:56:13Z |
| issue-492-comments | /repos/Tencent/teamai-cli/issues/492/comments?per_page=30 | 200 | 2,578 | ad406eac7cca | 04:56:13Z |
| issue-360-comments | /repos/Tencent/teamai-cli/issues/360/comments?per_page=30 | 200 | 3,925 | 34c7bd079a07 | 04:56:13Z |
| issue-17-comments | /repos/Tencent/teamai-cli/issues/17/comments?per_page=30 | 200 | 27,752 | 4f4cca0bc448 | 04:56:14Z |
| issue-459-comments | /repos/Tencent/teamai-cli/issues/459/comments?per_page=30 | 200 | 11,961 | 40c5048d20c3 | 04:56:14Z |
| issue-471-comments | /repos/Tencent/teamai-cli/issues/471/comments?per_page=30 | 200 | 2,842 | 91a54e310e13 | 04:56:14Z |
| ref-end | /repos/Tencent/teamai-cli/git/ref/heads/main | 200 | 334 | 32124af88bc3 | 05:01:01Z |
401 응답 4건(branch protection · code scanning · dependabot · secret scanning)은 unknown이지 0이 아니다. 전체 해시·바이트·관측 시각은 evidence/index.json이 가리키는 github-sources.jsonl에 있다.
선택 파일 원장 (30 · 전부 full)
| path | 범주 | object id | 줄 | bytes | coverage |
|---|---|---|---|---|---|
| README.md | docs | 7c0815bd35 | 273 | 18,909 | full |
| LICENSE | docs | 935ff4be4d | 13 | 1,418 | full |
| package.json | manifest | 6215a0c345 | 86 | 2,028 | full |
| AGENTS.md | docs | b3a746b85c | 24 | 1,293 | full |
| .github/workflows/ci.yml | ci | aa3b9b57c9 | 151 | 4,250 | full |
| .github/workflows/release.yml | ci | 40c6711824 | 88 | 2,890 | full |
| .github/CONTRIBUTING.md | docs | 99aa271284 | 80 | 2,722 | full |
| docs/providers.md | docs | e8ba8e7f3a | 313 | 17,166 | full |
| docs/designs/data-directory-layout.md | docs | 82efa66220 | 283 | 17,058 | full |
| docs/usage-guide.md | docs | b932314de9 | 1,616 | 89,207 | full |
| CHANGELOG.md | docs | dd9a88c18a | 1,236 | 76,704 | full |
| src/index.ts | source | 6432abff5a | 1,122 | 48,616 | full |
| src/init.ts | source | bb3b11b189 | 1,457 | 57,605 | full |
| src/pull.ts | source | 73cc9dd4af | 1,720 | 75,884 | full |
| src/push.ts | source | 42ac69bf98 | 972 | 39,297 | full |
| src/hooks.ts | source | 1f038375e1 | 1,222 | 49,761 | full |
| src/hook-handlers.ts | source | 5f37a0c266 | 518 | 23,351 | full |
| src/config.ts | source | 8a6dc74a6f | 435 | 17,931 | full |
| src/api-key.ts | source | 71ccfcf6fd | 53 | 1,905 | full |
| src/usage-tracker.ts | source | c28910c159 | 450 | 14,944 | full |
| src/session-collector.ts | source | 13d62b6fed | 237 | 8,776 | full |
| src/utils/ai-client.ts | source | bee9b63f36 | 384 | 13,451 | full |
| src/machine-id.ts | source | 29e5aa5e99 | 117 | 4,205 | full |
| src/bootstrap.ts | source | 6d1ae31836 | 236 | 9,782 | full |
| src/__tests__/init.test.ts | test | 8d4f8dcc2c | 621 | 20,520 | full |
| src/__tests__/hooks.test.ts | test | 9076077a25 | 761 | 32,068 | full |
| src/__tests__/pull-skip-sync.test.ts | test | 77ed80503b | 432 | 14,852 | full |
| src/__tests__/push-pending-pr.test.ts | test | e1f85d57ad | 479 | 19,211 | full |
| vitest.config.ts | ci | 70a5c7c2de | 17 | 534 | full |
| vitest.e2e.config.ts | ci | 824488e691 | 20 | 641 | full |
누락·경계
- not-inspected — 504 of 534 blobs were inventoried (path, mode, object id, size) but not read. repository-root default bound of 30 substantive files
- not-inspected — src/local-agent.ts (3429 lines), src/types.ts, src/recall.ts, src/codebase*.ts, src/dashboard*.ts, src/digest.ts, src/providers/*, src/wiki-engine/*, src/utils/* except ai-client.ts, 245 of 249 test files. bounded inspection; conclusions about these modules are marked unknown or project-stated
- binary-or-asset — assets/*.svg (3 files). authored media not reproduced; SVG is never inlined (active-content isolation)
- generated — package-lock.json (214,528 bytes). lockfile inventoried only; dependency facts taken from package.json and the GitHub SBOM endpoint
- api-denied — branch protection, code-scanning, Dependabot and secret-scanning alert endpoints (HTTP 401 unauthenticated). recorded as unknown, never zero
- pagination — releases/tags (10 each), commits (30), workflow runs (20), issues/pulls (30 per state), contributors (30), issue comments (6 issues). bounded single-page samples; not the full population
정적 전용 영수증
| 행위 | 수행 |
|---|---|
| checkout / worktree | no |
| dependency install / build / test | no |
| run / import / container | no |
| hooks / submodules / LFS / filters / textconv | no |
| execution-observed 증거 | unavailable (0건) |
다음에 깨질 곳: 이 저장소는 하루 3.5건 릴리즈 속도라 이 문서의 file:line은 핀 커밋 34e1cfb35181에서만 유효하다. 재검토 시 hook-dispatch-cli.ts·contribute-check.ts·update.ts·local-agent.ts를 우선 정독하면 unknown 4건이 닫힌다.