Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,7 @@ GitHub Copilot CLI is supported for its official custom-instructions, Rules, Ski
- **Custom agents.** Team agents become official `<name>.agent.md` profiles under `$COPILOT_HOME/agents/` or `.github/agents/`. TeamAI maps compatible tool names onto Copilot's primary aliases, preserves Copilot-only frontmatter through `tool_extras.copilot`, and removes only profiles that match team agents or the built-in recall profile. User-authored profiles remain untouched. See [GitHub's custom-agent configuration](https://docs.github.com/en/copilot/reference/custom-agents-configuration).
- **Team Context recall.** The built-in `teamai-recall.agent.md` profile receives only `execute`, `read`, and `search`. It invokes the existing `teamai recall` pipeline, so Copilot can retrieve learnings, codebase evidence, and teamwiki results without copying or creating a second knowledge store.
- **Docs and Env.** Team docs sync to the configured local docs directory (`~/.teamai/docs` by default, or the project-relative equivalent in project scope). Team env values sync to the scope's managed `env.sh`; launch Copilot from a shell that has sourced that file. TeamAI does not copy environment values into Copilot configuration.
- **Hooks.** TeamAI writes a dedicated version-1 hook file at `$COPILOT_HOME/hooks/teamai.json` or `.github/hooks/teamai.json`. It uses Copilot's VS Code-compatible PascalCase events (`SessionStart`, `UserPromptSubmit`, `PostToolUse`, and `Stop`) so hook payloads retain the snake_case fields consumed by TeamAI, and emits `bash`, `powershell`, and fallback `command` fields. The file is reconciled idempotently while preserving unrelated entries. TeamAI never edits Copilot's `settings.json`.
- **Hooks and private telemetry.** TeamAI writes a dedicated version-1 hook file at `$COPILOT_HOME/hooks/teamai.json` or `.github/hooks/teamai.json`. It uses Copilot's VS Code-compatible PascalCase events (`SessionStart`, `UserPromptSubmit`, `PostToolUse`, `Stop`, and `SessionEnd`) so hook payloads retain the snake_case fields consumed by TeamAI, and emits `bash`, `powershell`, and fallback `command` fields. Session IDs, skill usage, prompt counts, lifecycle state, and final token totals feed the local dashboard. Copilot prompt text, assistant output, transcript paths, and request metadata are never stored; if final token counters are absent, the session is still recorded without token data. For resumed sessions, TeamAI records a path-free log byte boundary at SessionStart and captures a marker already present only when it is neither closed nor claimed by the previous run. Shutdown counters must link to that marker or to one written after the boundary. If a marker appears only after SessionStart and SessionEnd has no provider timestamp, its run cannot be proven and the session remains recorded without token data. The file is reconciled idempotently while preserving unrelated entries. TeamAI never edits Copilot's `settings.json`.
- **MCP.** `teamai pull` and `teamai mcp inject` merge local and remote servers into `$COPILOT_HOME/mcp-config.json` or `.github/mcp.json` using Copilot's native schema. TeamAI tracks ownership outside the Copilot file, so repeated pulls are idempotent and `mcp remove` or uninstall removes only TeamAI-owned entries. Hand-authored servers and `settings.json` remain unchanged.

Team hooks still come from the team's `hooks/hooks.yaml`: edit that source in the team repository and use the normal pull/push workflow. TeamAI does not reverse-import arbitrary native hook entries from a Copilot configuration file.
Expand Down
2 changes: 1 addition & 1 deletion docs/usage-guide.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ GitHub Copilot CLI 已支持其官方自定义指令、Rules、Skills、自定
- **自定义 Agents。** 团队 Agents 会转换为 `$COPILOT_HOME/agents/` 或 `.github/agents/` 下的官方 `<name>.agent.md` 配置。TeamAI 将兼容的工具名映射为 Copilot 主别名,通过 `tool_extras.copilot` 保留 Copilot 专属 frontmatter,并且只删除与团队 Agent 或内置 recall 配置匹配的文件;用户自建配置保持不变。详见 [GitHub 自定义 Agent 配置](https://docs.github.com/zh/copilot/reference/custom-agents-configuration)。
- **Team Context recall。** 内置 `teamai-recall.agent.md` 只获得 `execute`、`read` 和 `search`。它调用现有的 `teamai recall` 流程,让 Copilot 检索 learnings、codebase 证据和 teamwiki 结果,而不会复制或创建第二套知识库。
- **Docs 和 Env。** 团队 Docs 同步到配置的本地文档目录(默认 `~/.teamai/docs`;project scope 使用项目内对应路径)。团队环境变量同步到该作用域由 TeamAI 管理的 `env.sh`;请从已 source 此文件的 shell 启动 Copilot。TeamAI 不会把环境变量值复制到 Copilot 配置中。
- **Hooks。** TeamAI 在 `$COPILOT_HOME/hooks/teamai.json` 或 `.github/hooks/teamai.json` 写入独立的 version-1 Hook 文件,使用 Copilot 与 VS Code 兼容的 PascalCase 事件(`SessionStart`、`UserPromptSubmit`、`PostToolUse` 和 `Stop`),从而保留 TeamAI 所需的 snake_case Hook 负载字段,并生成 `bash`、`powershell` 和后备 `command` 字段。文件会被幂等合并,且保留无关条目。TeamAI 从不修改 Copilot 的 `settings.json`。
- **Hooks 与隐私遥测。** TeamAI 在 `$COPILOT_HOME/hooks/teamai.json` 或 `.github/hooks/teamai.json` 写入独立的 version-1 Hook 文件,使用 Copilot 与 VS Code 兼容的 PascalCase 事件(`SessionStart`、`UserPromptSubmit`、`PostToolUse`、`Stop` 和 `SessionEnd`),从而保留 TeamAI 所需的 snake_case Hook 负载字段,并生成 `bash`、`powershell` 和后备 `command` 字段。会话 ID、Skill 使用、提示次数、生命周期状态和最终 Token 总数会进入本地 Dashboard;Copilot 提示原文、助手输出、Transcript 路径和请求元数据绝不会被保存。若最终 Token 计数不可用,会话仍会被记录,但不包含 Token 数据。对于恢复的会话,TeamAI 在 SessionStart 时保存不含路径的日志字节边界;只有此前的运行标记尚未关闭、且未被上次运行使用时,才会采纳该标记。关闭计数必须关联这个标记或边界之后写入的标记。若标记仅在 SessionStart 之后出现,而 SessionEnd 没有提供方时间戳,则无法确认它属于本次运行;会话仍会被记录,但不包含 Token 数据。文件会被幂等合并,且保留无关条目。TeamAI 从不修改 Copilot 的 `settings.json`。
- **MCP。** `teamai pull` 和 `teamai mcp inject` 使用 Copilot 原生结构,把本地与远程 Server 合并到 `$COPILOT_HOME/mcp-config.json` 或 `.github/mcp.json`。归属信息保存在 Copilot 文件之外,因此重复 pull 保持幂等,`mcp remove` 或卸载只会移除 TeamAI 管理的条目;手写 Server 与 `settings.json` 均保持不变。

团队 Hooks 仍以团队仓库中的 `hooks/hooks.yaml` 为来源:直接编辑该文件,再使用正常的 pull/push 流程。TeamAI 不会从 Copilot 配置文件反向导入任意原生 Hook 条目。
Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/builtin-hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ describe('builtinHookDefs — unified built-in hook model', () => {
]);
});

it('adds a lifecycle-complete SessionEnd hook only for Copilot', () => {
const defs = builtinHookDefs('copilot');
expect(defs).toHaveLength(7);
expect(defs.at(-1)).toEqual(expect.objectContaining({
event: 'SessionEnd',
matcher: '*',
timeout: 15,
command: expect.stringContaining('hook-dispatch session-end --tool copilot'),
}));
expect(builtinHookDefs('claude')).toHaveLength(6);
});

it('Claude defs carry no timeout; Cursor defs carry per-hook timeouts', () => {
expect(builtinHookDefs('claude').every((d) => d.timeout === undefined)).toBe(true);
const cursor = builtinHookDefs('cursor');
Expand Down
6 changes: 5 additions & 1 deletion src/__tests__/copilot-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ describe('GitHub Copilot adapter', () => {
expect(parsed.hooks.UserPromptSubmit).toBeDefined();
expect(parsed.hooks.PostToolUse).toBeDefined();
expect(parsed.hooks.Stop).toBeDefined();
expect(parsed.hooks.SessionEnd).toBeDefined();
expect(parsed.hooks.SessionStart[0]).toEqual(expect.objectContaining({
type: 'command',
bash: expect.stringContaining('teamai hook-dispatch session-start --tool copilot'),
Expand All @@ -232,7 +233,10 @@ describe('GitHub Copilot adapter', () => {
timeoutSec: expect.any(Number),
}));
expect(parsed.hooks.PostToolUse).toEqual(expect.arrayContaining([
expect.objectContaining({ matcher: 'Skill' }),
expect.objectContaining({
matcher: 'skill',
command: expect.stringContaining('--matcher Skill'),
}),
expect.objectContaining({ matcher: 'TodoWrite' }),
]));
expect(parsed.hooks.PostToolUse.some((entry) => entry.matcher === undefined)).toBe(true);
Expand Down
Loading
Loading