Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e514d36
docs:知识库飞轮系统设计roadmap文档
Jun 5, 2026
7455087
feat: phase 1 - agents support, multi-index search, recall subagent &…
Jun 7, 2026
941553f
docs: update roadmap文档
m0Nst3r873 Jun 8, 2026
7e9c7de
feat(search): P1.4 domain inference + search weighting
m0Nst3r873 Jun 8, 2026
16f7ae0
test(validation): add Phase 1 E2E suite and acceptance report
m0Nst3r873 Jun 8, 2026
b2aef31
docs: update roadmap — add P4.6 learning promotion mechanism
m0Nst3r873 Jun 8, 2026
f7dfaad
Merge pull request #1 from m0Nst3r873/worktree-feature+p1.4-domain-in…
m0Nst3r873 Jun 8, 2026
4ef5a07
docs(validation): update phase1 report to reflect E2E bug fix
m0Nst3r873 Jun 8, 2026
b4e4db6
docs(validation): add runtime evidence appendix to phase1 report
m0Nst3r873 Jun 8, 2026
a8a6310
feat(search): query-aware domain weights + IDF scoring (v4 index)
m0Nst3r873 Jun 8, 2026
f95fe7c
feat(import): add teamai import command — Phase 0 cold-start + P4.4 M…
m0Nst3r873 Jun 9, 2026
fec7ced
Merge pull request #2 from m0Nst3r873/worktree-feature+phase0-p44-import
m0Nst3r873 Jun 9, 2026
e791d8b
fix(import): support claude-internal CLI + gh REST API fallback + rea…
m0Nst3r873 Jun 9, 2026
779c4cb
Merge pull request #3 from m0Nst3r873/worktree-feature+phase0-p44-import
m0Nst3r873 Jun 9, 2026
c9eb178
fix(codebase): require file-path prefix in module descriptions for ag…
m0Nst3r873 Jun 9, 2026
336b600
feat(codebase): upgrade workspace + MR prompts to A1-level documentat…
m0Nst3r873 Jun 9, 2026
d76f4a8
docs(validation): replace A1+A4 codebase docs with real teamai-cli ge…
m0Nst3r873 Jun 9, 2026
588ddf0
feat(mr-hint): add SessionStart hook to hint AI about unimported merg…
m0Nst3r873 Jun 10, 2026
d508520
feat(mr-hint): add GitHub REST API fallback when gh CLI unavailable
m0Nst3r873 Jun 10, 2026
2cbc278
docs(validation): update public acceptance report for P4.4 mr-hint tr…
m0Nst3r873 Jun 10, 2026
771a875
feat(ai-client): support login shell PATH + extend CLI candidates
m0Nst3r873 Jun 10, 2026
c1e5e13
docs(validation): update A4 with real before/after codebase demo
m0Nst3r873 Jun 10, 2026
bfba491
Merge pull request #4 from m0Nst3r873/worktree-feature+p4-4-mr-hint
m0Nst3r873 Jun 10, 2026
b4bbb11
fix(providers): add TGit REST API fallback + multi-shell CLI detection
m0Nst3r873 Jun 10, 2026
29da3ee
docs: 验收文档typo更正
m0Nst3r873 Jun 10, 2026
0f58dfc
fix(ai-client): multi-CLI compat + shell injection hardening
m0Nst3r873 Jun 10, 2026
a166ebd
feat(codebase): align with llm-wiki — frontmatter / index / lint / mu…
m0Nst3r873 Jun 10, 2026
a456f9a
Merge pull request #5 from m0Nst3r873/worktree-codebase-llm-wiki-opti…
m0Nst3r873 Jun 10, 2026
d2fca83
feat(search): codebase-index.md high-weight + skip codebase.md
m0Nst3r873 Jun 10, 2026
ff331c2
docs(validation): refresh A1/A4 with llm-wiki-optimized codebase output
m0Nst3r873 Jun 10, 2026
3161efb
docs(validation): replace codebase-after full content with before/aft…
m0Nst3r873 Jun 10, 2026
1777051
docs(roadmap): add Phase 6 — Phase 5 hardening
m0Nst3r873 Jun 11, 2026
888aa5e
feat(import): Phase 5 — team-level codebase aggregation
m0Nst3r873 Jun 11, 2026
8ce2d96
Merge pull request #6 from m0Nst3r873/feature/team-codebase-phase5
m0Nst3r873 Jun 11, 2026
4651a7c
feat(agents): multi-CLI subagent sync + security hardening
m0Nst3r873 Jun 11, 2026
28cacd8
Merge pull request #7 from m0Nst3r873/worktree-feature+multi-cli-suba…
m0Nst3r873 Jun 11, 2026
2580f1e
feat: Phase 6 — Phase 5 hardening pass
m0Nst3r873 Jun 11, 2026
8ab73b7
Merge pull request #8 from m0Nst3r873/feature/team-codebase-phase6
m0Nst3r873 Jun 11, 2026
6e435aa
chore: stop tracking local drafts (roadmap, validation, .codebuddy)
m0Nst3r873 Jun 11, 2026
044e86e
docs(readme): trim subagent phase notes and add Phase 5/6 commands
m0Nst3r873 Jun 11, 2026
bad5cdd
fix(p5-p6): address audit findings — 2 blockers, 1 major, 5 medium
m0Nst3r873 Jun 11, 2026
f533ebc
Merge pull request #9 from m0Nst3r873/worktree-fix-p5-p6-audit
m0Nst3r873 Jun 11, 2026
7a74f92
fix(test): add missing `type` field to recall.test.ts SearchIndexEntr…
m0Nst3r873 Jun 11, 2026
8325e88
fix(test): resolve cross-platform path assertion failures in review-c…
m0Nst3r873 Jun 12, 2026
68f5850
Merge remote-tracking branch 'upstream/main'
m0Nst3r873 Jun 15, 2026
aa869d9
feat(contribute-check): Phase 2 知识库空白感知 + git commit 降权
m0Nst3r873 Jun 15, 2026
c828c44
Merge pull request #10 from m0Nst3r873/worktree-phase2-contribute-check
m0Nst3r873 Jun 15, 2026
2fb0143
chore: MVP 发布前清理——移除 --limit 死选项,import 完成后提示 push
m0Nst3r873 Jun 15, 2026
2ff41ea
chore: AI 调用超时从 2min 调整为 12min
m0Nst3r873 Jun 15, 2026
f1df9ef
fix(test): codebase-lint scaffold 使用 tmpdir 作为 source 路径
m0Nst3r873 Jun 15, 2026
8872c60
fix: 修复 MVP 6 个功能闭环问题
m0Nst3r873 Jun 15, 2026
c73f743
feat(tgit): 支持工蜂 group ID 直接用于 --from-org + fallback 策略
m0Nst3r873 Jun 16, 2026
7fcf5ae
feat: 部署 teamai-recall 规则到所有工具 rules/ 目录
m0Nst3r873 Jun 16, 2026
bedaf29
fix: 修复 import --from-org 产物路径、domain 映射和 AI 废话问题
Jun 16, 2026
3550651
feat: recall rules 明确团队知识库必须通过检索 subagent 访问
Jun 16, 2026
a4ad3c1
feat: recall rules 约束 agent 调用顺序 + codebase docs 搜索放宽
Jun 16, 2026
139ffdc
fix: 统一 user/project scope 索引路径与 hook 注入
Jun 16, 2026
d60d7e8
Merge remote-tracking branch 'upstream/main'
m0Nst3r873 Jun 22, 2026
554cd99
Merge branch 'main' of github.com:m0Nst3r873/teamai-cli
m0Nst3r873 Jun 22, 2026
73ee184
fix(uninstall): clean all teamai sections from CLAUDE.md
m0Nst3r873 Jun 22, 2026
33ac14c
feat(ci): add `teamai ci extract-mr` for MR knowledge extraction pipe…
m0Nst3r873 Jun 22, 2026
3e3ebf5
fix(import-mr): strip AI preamble before learning frontmatter
m0Nst3r873 Jun 22, 2026
c3c712e
fix(tgit): use MR global id for Notes API and iid query for MR detail
m0Nst3r873 Jun 22, 2026
453ee80
fix(ci): wrap learning content in markdown code block for better comm…
m0Nst3r873 Jun 22, 2026
f0e3c98
Merge remote-tracking branch 'upstream/main'
m0Nst3r873 Jun 23, 2026
1baa2d3
feat(ci): add reaction/reject interaction for MR knowledge suggestions
m0Nst3r873 Jun 23, 2026
b42bdc7
fix(tgit): use emoji reaction (🚫 no, id=8) for reject instead of reso…
m0Nst3r873 Jun 23, 2026
5a856d9
fix(ci): auto-configure git user from token API to pass committer-check
m0Nst3r873 Jun 23, 2026
247302f
fix: correct TGit reject emoji description to ☝️ (id=8, 竖起食指)
m0Nst3r873 Jun 23, 2026
6f93a5b
Merge remote-tracking branch 'upstream/main'
m0Nst3r873 Jun 23, 2026
7b1c3cc
Delete RELEASE_NOTE_v0.16.8.md
m0Nst3r873 Jun 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ __pycache__/
.gstack/
.worktrees/
.codebuddy/
.teamai/

# Local-only drafts and scratch notes (kept out of the open-source repo)
eval/
Expand Down
4 changes: 2 additions & 2 deletions examples/ci/coding-ci-mr-extract.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ stages:
--write-mode direct
- |
cd team-repo
git config user.name "teamai-ci"
git config user.email "teamai-ci@tencent.com"
# git user.name/email 由 teamai ci extract-mr 自动从 token 对应账号获取
# 确保 TAI_PAT_TOKEN 对应的账号对知识仓库有 push 权限即可
git add learnings/ docs/ .teamai/
git diff --cached --quiet || \
git commit -m "[teamai] Extract knowledge from MR !${CI_MERGE_REQUEST_IID}"
Expand Down
63 changes: 63 additions & 0 deletions src/__tests__/read-rejections.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { describe, it, expect } from 'vitest';
import { extractMarkerId, shouldWrite } from '../ci/read-rejections.js';
import type { RejectionResult } from '../ci/read-rejections.js';

describe('extractMarkerId', () => {
it('提取 learning marker', () => {
expect(extractMarkerId('<!-- teamai:ci-extract:learning -->\nsome content')).toBe('learning');
});

it('提取 suggestion marker', () => {
expect(extractMarkerId('<!-- teamai:ci-extract:suggestion:1 -->\ncontent')).toBe('suggestion:1');
});

it('提取 suggestion:2 marker', () => {
expect(extractMarkerId('<!-- teamai:ci-extract:suggestion:2 -->')).toBe('suggestion:2');
});

it('无 marker 返回 null', () => {
expect(extractMarkerId('普通 comment 内容')).toBeNull();
});

it('其他 teamai marker 不匹配', () => {
expect(extractMarkerId('<!-- teamai:ci-extract -->')).toBeNull();
});
});

describe('shouldWrite', () => {
const rejections: RejectionResult = {
rejectedIds: new Set(['suggestion:2']),
approvedIds: new Set(['learning', 'suggestion:1']),
allIds: new Set(['learning', 'suggestion:1', 'suggestion:2']),
};

describe('GitHub (默认写入,👎 = reject)', () => {
it('不在 rejectedIds 中 → 写入', () => {
expect(shouldWrite('learning', rejections, 'github')).toBe(true);
expect(shouldWrite('suggestion:1', rejections, 'github')).toBe(true);
});

it('在 rejectedIds 中 → 不写入', () => {
expect(shouldWrite('suggestion:2', rejections, 'github')).toBe(false);
});

it('未知 id(不在任何集合中)→ 写入(默认)', () => {
expect(shouldWrite('suggestion:99', rejections, 'github')).toBe(true);
});
});

describe('TGit (默认写入,🚫 emoji = reject)', () => {
it('不在 rejectedIds 中 → 写入', () => {
expect(shouldWrite('learning', rejections, 'tgit')).toBe(true);
expect(shouldWrite('suggestion:1', rejections, 'tgit')).toBe(true);
});

it('在 rejectedIds 中 → 不写入', () => {
expect(shouldWrite('suggestion:2', rejections, 'tgit')).toBe(false);
});

it('未知 id(不在任何集合中)→ 写入(默认)', () => {
expect(shouldWrite('suggestion:99', rejections, 'tgit')).toBe(true);
});
});
});
124 changes: 108 additions & 16 deletions src/ci/extract-mr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { appendPendingReview } from '../review-store.js';
import { pushRepoDirectly } from '../utils/git.js';
import { log } from '../utils/logger.js';
import type { LearningDraft, CodebaseSuggestion } from '../types.js';
import { postOrUpdateMrComment } from './mr-comment.js';
import { postOrUpdateMrComment, postIndividualComments, parseMrUrl } from './mr-comment.js';
import { readRejections, shouldWrite } from './read-rejections.js';

// ─── 类型 ────────────────────────────────────────────────

Expand All @@ -31,6 +32,62 @@ export interface CiExtractMrOptions {
writeMode?: 'direct' | 'pending-review';
output?: string;
dryRun?: boolean;
individualComments?: boolean;
}

// ─── Git User 配置 ───────────────────────────────────────

/**
* 自动配置 git user.name 和 user.email。
*
* 通过 provider API 获取当前 token 对应的用户信息:
* - GitHub: GITHUB_TOKEN → GET /user
* - TGit: TAI_PAT_TOKEN → GET /api/v3/user
*/
async function configureGitUser(repoPath: string, provider: 'github' | 'tgit'): Promise<void> {
const { execFileSync } = await import('node:child_process');
let name = 'teamai-ci';
let email = 'teamai-ci@noreply';

try {
if (provider === 'github') {
const token = process.env['GITHUB_TOKEN'];
if (token) {
const resp = await fetch('https://api.github.com/user', {
headers: { Authorization: `Bearer ${token}`, 'User-Agent': 'teamai-cli' },
signal: AbortSignal.timeout(8000),
});
if (resp.ok) {
const user = (await resp.json()) as { login: string; email: string | null };
name = user.login;
email = user.email ?? `${user.login}@users.noreply.github.com`;
}
}
} else {
const token = process.env['TAI_PAT_TOKEN'];
if (token) {
const resp = await fetch('https://git.woa.com/api/v3/user', {
headers: { Authorization: `Bearer ${token}` },
signal: AbortSignal.timeout(8000),
});
if (resp.ok) {
const user = (await resp.json()) as { username: string; email: string };
name = user.username;
email = user.email;
}
}
}
} catch {
log.debug('无法获取用户信息,使用默认 git user');
}

try {
execFileSync('git', ['config', 'user.name', name], { cwd: repoPath, stdio: 'ignore' });
execFileSync('git', ['config', 'user.email', email], { cwd: repoPath, stdio: 'ignore' });
log.debug(`Git user: ${name} <${email}>`);
} catch {
log.debug('git config 失败(非 git 仓库),跳过');
}
}

// ─── 写入逻辑 ────────────────────────────────────────────
Expand Down Expand Up @@ -102,6 +159,8 @@ async function writeKnowledgeToRepo(
// 提交并推送
if (!dryRun && changedFiles.length > 0) {
try {
const provider = mrUrl.includes('github.com') ? 'github' as const : 'tgit' as const;
await configureGitUser(teamRepo, provider);
await pushRepoDirectly(teamRepo, `[teamai] CI extract knowledge from MR`, changedFiles);
log.success('已推送到团队仓库');
} catch (err) {
Expand Down Expand Up @@ -185,29 +244,62 @@ export async function ciExtractMr(opts: CiExtractMrOptions): Promise<void> {

// 执行 comment
if (opts.mode === 'comment' || opts.mode === 'both') {
const result = await postOrUpdateMrComment(
opts.url,
learning,
suggestions,
opts.commentMarker,
opts.dryRun,
);
if (result.created) {
log.success('MR comment 已发布');
if (opts.individualComments) {
const { posted } = await postIndividualComments(opts.url, learning, suggestions, opts.dryRun);
log.success(`已发布 ${posted} 条独立建议 comment`);
} else {
log.success('MR comment 已更新');
}
if (result.url) {
log.info(`Comment URL: ${result.url}`);
const result = await postOrUpdateMrComment(
opts.url,
learning,
suggestions,
opts.commentMarker,
opts.dryRun,
);
if (result.created) {
log.success('MR comment 已发布');
} else {
log.success('MR comment 已更新');
}
if (result.url) {
log.info(`Comment URL: ${result.url}`);
}
}
}

// 执行 write
if (opts.mode === 'write' || opts.mode === 'both') {
// 当使用 individual comments 时,读取 rejection 状态进行过滤
let filteredLearning = learning;
let filteredSuggestions = suggestions;

if (opts.individualComments && !opts.dryRun) {
const parsed = parseMrUrl(opts.url);
const rejections = await readRejections(opts.url);

if (rejections.allIds.size > 0) {
// 过滤 learning
if (learning && !shouldWrite('learning', rejections, parsed.provider)) {
log.info('Learning 被 reject,跳过写入');
filteredLearning = undefined;
}

// 过滤 suggestions
if (suggestions) {
filteredSuggestions = suggestions.filter((_, i) =>
shouldWrite(`suggestion:${i + 1}`, rejections, parsed.provider),
);
const rejected = suggestions.length - filteredSuggestions.length;
if (rejected > 0) {
log.info(`${rejected} 条 codebase 建议被 reject,已排除`);
}
}
}
}

await writeKnowledgeToRepo(
opts.teamRepo!,
learning,
suggestions,
filteredLearning,
filteredSuggestions,
opts.writeMode ?? 'direct',
opts.url,
opts.dryRun,
Expand Down
Loading
Loading