teamai-cli 0.25.0-beta.1 → 0.25.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +3 -2
- package/README.th.md +1 -1
- package/README.zh-CN.md +3 -2
- package/dist/index.js +400 -78
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
6
6
|
|
|
7
7
|
### ✨ Features
|
|
8
8
|
|
|
9
|
+
- `teamai remove` accepts `--force` to skip its confirmation prompt, spelled the same way as `teamai uninstall --force`. Without a TTY the prompt answers itself with no, so this is the only way to remove a resource from a script or a test (for [#591](https://github.com/Tencent/teamai-cli/issues/591)).
|
|
9
10
|
- MCP servers in `mcp/mcp.yaml` and hooks in `hooks/hooks.yaml` accept an optional `roles:` list and ship only to members holding one of those roles; a role change removes the previous role's entries on the next pull, and `teamai mcp list` / `teamai hooks list` show the restriction (for [#563](https://github.com/Tencent/teamai-cli/issues/563)).
|
|
10
11
|
- Agents can be scoped by role or project: `agents/<namespace>/` ships only to members whose `roles.yaml` / `projects.yaml` entry lists that namespace under a new optional `agents:` key, and a role change removes the previous namespaces' agents on the next pull (for [#563](https://github.com/Tencent/teamai-cli/issues/563)).
|
|
11
12
|
- First-class Kiro support: skills, steering rules, JSON subagents with CLI `agentSpawn` session-start hooks, and MCP sync to `.kiro/` (for [#500](https://github.com/Tencent/teamai-cli/issues/500)).
|
|
@@ -15,8 +16,10 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
15
16
|
|
|
16
17
|
### 🐛 Bug Fixes
|
|
17
18
|
|
|
19
|
+
- `teamai tags subscribe` and `teamai tags unsubscribe` now invalidate the pull revision cache, as `teamai skill exclude` already does, so the next `teamai pull` applies the new subscriptions instead of reporting "Already synced" when the team repo has not changed.
|
|
18
20
|
- `teamai pull` now deletes a tombstoned agent under all three render extensions, so the Codex `.toml` and Kiro `.json` copies of a removed agent no longer survive on other machines. The cleanup also runs when the team repo rev is unchanged, so an upgrade reaches machines that already pulled the tombstone with an older CLI. `teamai remove agents <name>` also honours `enabledAgents` and no longer deletes from excluded tools. Fixes [#576](https://github.com/Tencent/teamai-cli/issues/576).
|
|
19
21
|
- `teamai remove rules <name>` and `teamai remove skills <name>` now honour `enabledAgents` and leave excluded tools untouched, matching the whitelist `teamai pull` already applies when it cleans up a tombstoned resource. Fixes [#590](https://github.com/Tencent/teamai-cli/issues/590).
|
|
22
|
+
- `teamai pull` and `teamai mcp inject` no longer write team MCP servers into installed tools outside `enabledAgents` or listed in `disabledAgents`, the same gate skills, rules, agents and hooks already use. Servers injected before the upgrade are left in place, and `teamai uninstall` still removes them.
|
|
20
23
|
- `teamai import --cache-status` and `--cache-gc` now expose their existing JSON output through the CLI `--json` option.
|
|
21
24
|
- Course-correction matching normalizes prompts and keywords to Unicode NFC, so composed and decomposed accents match. Stored prompt summaries and the 60-second correction window are unchanged. Fixes [#573](https://github.com/Tencent/teamai-cli/issues/573).
|
|
22
25
|
- Course-correction detection matches keywords in space-separated scripts as whole words, so Spanish "segundo" no longer counts as `undo` (for [#564](https://github.com/Tencent/teamai-cli/issues/564)).
|
package/README.ja.md
CHANGED
|
@@ -266,7 +266,7 @@ teamai recall maintenance --update-quality # draft updates for stale skill
|
|
|
266
266
|
| `teamai remove <type> <name>` | リソースを削除し、MR を開く |
|
|
267
267
|
| `teamai session save` | プライバシー除去済みのセッション要約を月次ログへ記録(`--push` は `digest` へ供給) |
|
|
268
268
|
| `teamai digest` | チーム利用の週次 digest を生成 |
|
|
269
|
-
| `teamai doctor` |
|
|
269
|
+
| `teamai doctor` | 設定の問題を診断(`--json` で JSON 出力、CI・hook・agent 向け)|
|
|
270
270
|
| `teamai uninstall` | すべての teamai リソースと hooks を削除 |
|
|
271
271
|
|
|
272
272
|
## ライセンス
|
package/README.ko.md
CHANGED
|
@@ -266,7 +266,7 @@ teamai recall maintenance --update-quality # draft updates for stale skill
|
|
|
266
266
|
| `teamai remove <type> <name>` | 리소스를 제거하고 MR을 엽니다 |
|
|
267
267
|
| `teamai session save` | 개인정보를 제거한 세션 요약을 월별 로그에 기록 (`--push`는 `digest`에 공급) |
|
|
268
268
|
| `teamai digest` | 주간 팀 사용 digest 생성 |
|
|
269
|
-
| `teamai doctor` | 구성 문제 진단 |
|
|
269
|
+
| `teamai doctor` | 구성 문제 진단 (`--json`으로 JSON 출력, CI·hook·agent용) |
|
|
270
270
|
| `teamai uninstall` | 모든 teamai 리소스와 hooks 제거 |
|
|
271
271
|
|
|
272
272
|
## 라이선스
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://www.npmjs.com/package/teamai-cli)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
|
-
TeamAI manages your team's skills, rules, MCP, and knowledge across Claude Code, Codex, CodeBuddy, WorkBuddy, OpenCode, Cursor, and other AI agents.
|
|
14
|
+
TeamAI manages your team's skills, rules, MCP, and knowledge across Claude Code, Codex, GitHub Copilot CLI, CodeBuddy, WorkBuddy, OpenCode, Cursor, and other AI agents.
|
|
15
15
|
|
|
16
16
|
## Contributors
|
|
17
17
|
|
|
@@ -86,6 +86,7 @@ Once initialized, every AI session automatically pulls the latest skills / rules
|
|
|
86
86
|
<tr><td>Claude Code</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
87
87
|
<tr><td>Codex</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
88
88
|
<tr><td>Cursor</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
89
|
+
<tr><td>GitHub Copilot CLI</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td></tr>
|
|
89
90
|
<tr><td>CodeBuddy</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
90
91
|
<tr><td>WorkBuddy</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
91
92
|
<tr><td>OpenCode</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td></tr>
|
|
@@ -266,7 +267,7 @@ Insight into how the team actually uses its AI tools, and a starting point for t
|
|
|
266
267
|
| `teamai remove <type> <name>` | Remove a resource and open MR |
|
|
267
268
|
| `teamai session save` | Record a privacy-scrubbed session summary to a monthly log (`--push` feeds `digest`) |
|
|
268
269
|
| `teamai digest` | Generate weekly team usage digest |
|
|
269
|
-
| `teamai doctor` | Diagnose configuration issues |
|
|
270
|
+
| `teamai doctor` | Diagnose configuration issues (`--json` for CI, hooks and agents) |
|
|
270
271
|
| `teamai uninstall` | Remove all teamai resources and hooks |
|
|
271
272
|
|
|
272
273
|
## License
|
package/README.th.md
CHANGED
|
@@ -266,7 +266,7 @@ teamai recall maintenance --update-quality # draft updates for stale skill
|
|
|
266
266
|
| `teamai remove <type> <name>` | ลบทรัพยากรแล้วเปิด MR |
|
|
267
267
|
| `teamai session save` | บันทึกสรุปเซสชันที่ล้างข้อมูลส่วนตัวแล้วลงล็อกรายเดือน (`--push` จะป้อน `digest`) |
|
|
268
268
|
| `teamai digest` | สร้างสรุปการใช้งานทีมรายสัปดาห์ |
|
|
269
|
-
| `teamai doctor` | วินิจฉัยปัญหาคอนฟิก |
|
|
269
|
+
| `teamai doctor` | วินิจฉัยปัญหาคอนฟิก (`--json` แสดงผลเป็น JSON สำหรับ CI, hook และ agent) |
|
|
270
270
|
| `teamai uninstall` | ลบทรัพยากรและ hooks ของ teamai ทั้งหมด |
|
|
271
271
|
|
|
272
272
|
## ใบอนุญาต
|
package/README.zh-CN.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://www.npmjs.com/package/teamai-cli)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
|
-
TeamAI 统一管理团队的 Skills、Rules、MCP 和知识,驾驭 Claude Code、Codex、CodeBuddy、WorkBuddy、OpenCode、Cursor 等 AI Agents。
|
|
14
|
+
TeamAI 统一管理团队的 Skills、Rules、MCP 和知识,驾驭 Claude Code、Codex、GitHub Copilot CLI、CodeBuddy、WorkBuddy、OpenCode、Cursor 等 AI Agents。
|
|
15
15
|
|
|
16
16
|
## 贡献者
|
|
17
17
|
|
|
@@ -86,6 +86,7 @@ teamai init https://github.com/yourorg/yourrepo --scope user
|
|
|
86
86
|
<tr><td>Claude Code</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
87
87
|
<tr><td>Codex</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
88
88
|
<tr><td>Cursor</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
89
|
+
<tr><td>GitHub Copilot CLI</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td><td align="center">—</td></tr>
|
|
89
90
|
<tr><td>CodeBuddy</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
90
91
|
<tr><td>WorkBuddy</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td></tr>
|
|
91
92
|
<tr><td>OpenCode</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">✓</td><td align="center">—</td><td align="center">—</td><td align="center">—</td></tr>
|
|
@@ -266,7 +267,7 @@ teamai recall maintenance --update-quality # 为过时 skills / docs 生
|
|
|
266
267
|
| `teamai remove <type> <name>` | 删除资源并创建 MR |
|
|
267
268
|
| `teamai session save` | 将脱敏后的 session 摘要记录到月度日志(`--push` 可喂给 `digest`) |
|
|
268
269
|
| `teamai digest` | 生成团队周报 |
|
|
269
|
-
| `teamai doctor` |
|
|
270
|
+
| `teamai doctor` | 诊断配置问题(`--json` 输出 JSON,供 CI、hook 与 agent 消费)|
|
|
270
271
|
| `teamai uninstall` | 移除所有 teamai 资源和 hooks |
|
|
271
272
|
|
|
272
273
|
## 许可证
|
package/dist/index.js
CHANGED
|
@@ -503,6 +503,7 @@ __export(types_exports, {
|
|
|
503
503
|
CONTRIBUTE_SKILL_BONUS: () => CONTRIBUTE_SKILL_BONUS,
|
|
504
504
|
CONTRIBUTE_SMART_THRESHOLD: () => CONTRIBUTE_SMART_THRESHOLD,
|
|
505
505
|
CONTRIBUTE_TOOLERROR_TIERS: () => CONTRIBUTE_TOOLERROR_TIERS,
|
|
506
|
+
COPILOT_TOOL_ID: () => COPILOT_TOOL_ID,
|
|
506
507
|
CORRECTION_KEYWORDS: () => CORRECTION_KEYWORDS,
|
|
507
508
|
CORRECTION_WINDOW_MS: () => CORRECTION_WINDOW_MS,
|
|
508
509
|
CultureCompanySchema: () => CultureCompanySchema,
|
|
@@ -556,6 +557,7 @@ __export(types_exports, {
|
|
|
556
557
|
areTeamHooksDisabled: () => areTeamHooksDisabled,
|
|
557
558
|
emptyTokenUsage: () => emptyTokenUsage,
|
|
558
559
|
getConfigPath: () => getConfigPath,
|
|
560
|
+
getCopilotHome: () => getCopilotHome,
|
|
559
561
|
getDataHome: () => getDataHome,
|
|
560
562
|
getEnvBackupPath: () => getEnvBackupPath,
|
|
561
563
|
getHooksSharing: () => getHooksSharing,
|
|
@@ -590,6 +592,7 @@ __export(types_exports, {
|
|
|
590
592
|
resolveCoAuthor: () => resolveCoAuthor,
|
|
591
593
|
resolveHookScope: () => resolveHookScope,
|
|
592
594
|
resolveLegacyProjectHookScope: () => resolveLegacyProjectHookScope,
|
|
595
|
+
resolveToolBaseDir: () => resolveToolBaseDir,
|
|
593
596
|
scopedToolPaths: () => scopedToolPaths,
|
|
594
597
|
totalTokens: () => totalTokens,
|
|
595
598
|
usesReportsBranch: () => usesReportsBranch
|
|
@@ -699,6 +702,14 @@ function resolveBaseDir(localConfig) {
|
|
|
699
702
|
}
|
|
700
703
|
return getUserHome();
|
|
701
704
|
}
|
|
705
|
+
function getCopilotHome(env = process.env) {
|
|
706
|
+
const configured = env.COPILOT_HOME?.trim();
|
|
707
|
+
return configured ? path3.resolve(configured) : path3.join(getUserHome(), DEFAULT_COPILOT_HOME);
|
|
708
|
+
}
|
|
709
|
+
function resolveToolBaseDir(tool, localConfig) {
|
|
710
|
+
if (tool === COPILOT_TOOL_ID && localConfig.scope === "user") return getCopilotHome();
|
|
711
|
+
return resolveBaseDir(localConfig);
|
|
712
|
+
}
|
|
702
713
|
function isAgentDisabled(localConfig, tool) {
|
|
703
714
|
return localConfig.disabledAgents?.includes(tool) ?? false;
|
|
704
715
|
}
|
|
@@ -719,7 +730,8 @@ function scopedToolPaths(teamConfig, localConfig) {
|
|
|
719
730
|
...paths,
|
|
720
731
|
...us.skills !== void 0 ? { skills: us.skills } : {},
|
|
721
732
|
...us.rules !== void 0 ? { rules: us.rules } : {},
|
|
722
|
-
...us.agents !== void 0 ? { agents: us.agents } : {}
|
|
733
|
+
...us.agents !== void 0 ? { agents: us.agents } : {},
|
|
734
|
+
...us.hooks !== void 0 ? { hooks: us.hooks } : {}
|
|
723
735
|
};
|
|
724
736
|
}
|
|
725
737
|
return out;
|
|
@@ -795,7 +807,7 @@ function getPushignorePath() {
|
|
|
795
807
|
function areTeamHooksDisabled() {
|
|
796
808
|
return process.env.TEAMAI_HOOKS_DISABLED === "1" || process.env.TEAMAI_HOOKS_DISABLED === "true";
|
|
797
809
|
}
|
|
798
|
-
var ToolPathsSchema, ScopeEnum, SharingConfigSchema, SourceConfigSchema, SOURCE_PULL_TTL_MS, TEAMAI_SOURCES_DIR, TeamaiConfigSchema, MemberConfigSchema, LocalConfigSchema, PendingPushItemSchema, PendingPushSchema, StateSchema, RESOURCE_TYPES, TEAMAI_RULES_START, TEAMAI_RULES_END, TEAMAI_HOOK_DESCRIPTION_PREFIX, TEAMAI_CUSTOM_HOOK_PREFIX, TEAMAI_AGENT_HOOK_PREFIX, TEAMAI_ENV_START, TEAMAI_ENV_END, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, TEAMAI_RECALL_RULES_START, TEAMAI_RECALL_RULES_END, SKILL_NAME_REGEX, UsageEventSchema, DASHBOARD_DEFAULT_PORT, DASHBOARD_IDLE_TIMEOUT_MS, DASHBOARD_STALE_TIMEOUT_MS, DASHBOARD_COMPACTION_THRESHOLD, DASHBOARD_STOPPED_DISPLAY_MS, DASHBOARD_PID_CHECK_INTERVAL_MS, CORRECTION_WINDOW_MS, CORRECTION_KEYWORDS, INTERVENTION_SCAN_MAX_BYTES, TRANSCRIPT_INTERRUPT_PREFIX, TRANSCRIPT_SYSTEM_PREFIXES, TRANSCRIPT_REJECT_MARKERS, CONTRIBUTE_BASE_THRESHOLD, CONTRIBUTE_SMART_THRESHOLD, CONTRIBUTE_INTERRUPT_WEIGHT, CONTRIBUTE_REJECT_WEIGHT, CONTRIBUTE_CORRECTION_WEIGHT, CONTRIBUTE_TOOLERROR_TIERS, CONTRIBUTE_SKILL_BONUS, CONTRIBUTE_DIVERSITY_BONUS_MAX, CONTRIBUTE_FASTPATH_TTL_MS, CONTRIBUTE_KNOWLEDGE_GAP_BONUS, CONTRIBUTE_LOW_QUALITY_BONUS, CONTRIBUTE_LOW_QUALITY_THRESHOLD, CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT, SEARCH_INDEX_VERSION, CultureCompanySchema, CultureTeamSchema, CultureFrontmatterSchema, REPORTS_BRANCH, REPORTS_WORKTREE_DIRNAME, KNOWLEDGE_WORKTREE_DIRNAME, REPORTS_LOCK_FILENAME, BOOTSTRAP_LOCK_FILENAME, SYNC_LOCK_FILENAME;
|
|
810
|
+
var ToolPathsSchema, ScopeEnum, SharingConfigSchema, SourceConfigSchema, SOURCE_PULL_TTL_MS, TEAMAI_SOURCES_DIR, TeamaiConfigSchema, MemberConfigSchema, LocalConfigSchema, PendingPushItemSchema, PendingPushSchema, StateSchema, RESOURCE_TYPES, TEAMAI_RULES_START, TEAMAI_RULES_END, TEAMAI_HOOK_DESCRIPTION_PREFIX, TEAMAI_CUSTOM_HOOK_PREFIX, TEAMAI_AGENT_HOOK_PREFIX, TEAMAI_ENV_START, TEAMAI_ENV_END, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, TEAMAI_RECALL_RULES_START, TEAMAI_RECALL_RULES_END, SKILL_NAME_REGEX, UsageEventSchema, DASHBOARD_DEFAULT_PORT, DASHBOARD_IDLE_TIMEOUT_MS, DASHBOARD_STALE_TIMEOUT_MS, DASHBOARD_COMPACTION_THRESHOLD, DASHBOARD_STOPPED_DISPLAY_MS, DASHBOARD_PID_CHECK_INTERVAL_MS, CORRECTION_WINDOW_MS, CORRECTION_KEYWORDS, INTERVENTION_SCAN_MAX_BYTES, TRANSCRIPT_INTERRUPT_PREFIX, TRANSCRIPT_SYSTEM_PREFIXES, TRANSCRIPT_REJECT_MARKERS, CONTRIBUTE_BASE_THRESHOLD, CONTRIBUTE_SMART_THRESHOLD, CONTRIBUTE_INTERRUPT_WEIGHT, CONTRIBUTE_REJECT_WEIGHT, CONTRIBUTE_CORRECTION_WEIGHT, CONTRIBUTE_TOOLERROR_TIERS, CONTRIBUTE_SKILL_BONUS, CONTRIBUTE_DIVERSITY_BONUS_MAX, CONTRIBUTE_FASTPATH_TTL_MS, CONTRIBUTE_KNOWLEDGE_GAP_BONUS, CONTRIBUTE_LOW_QUALITY_BONUS, CONTRIBUTE_LOW_QUALITY_THRESHOLD, CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT, SEARCH_INDEX_VERSION, CultureCompanySchema, CultureTeamSchema, CultureFrontmatterSchema, COPILOT_TOOL_ID, DEFAULT_COPILOT_HOME, REPORTS_BRANCH, REPORTS_WORKTREE_DIRNAME, KNOWLEDGE_WORKTREE_DIRNAME, REPORTS_LOCK_FILENAME, BOOTSTRAP_LOCK_FILENAME, SYNC_LOCK_FILENAME;
|
|
799
811
|
var init_types = __esm({
|
|
800
812
|
"src/types.ts"() {
|
|
801
813
|
"use strict";
|
|
@@ -804,6 +816,8 @@ var init_types = __esm({
|
|
|
804
816
|
skills: z.string().optional(),
|
|
805
817
|
rules: z.string().optional(),
|
|
806
818
|
settings: z.string().optional(),
|
|
819
|
+
/** Standalone hooks file for tools that do not store hooks in settings. */
|
|
820
|
+
hooks: z.string().optional(),
|
|
807
821
|
claudemd: z.string().optional(),
|
|
808
822
|
/** Per-tool agents directory (Phase 1: teamai-recall subagent target).
|
|
809
823
|
* Optional — tools without subagent support omit this and agents sync skips them. */
|
|
@@ -826,7 +840,8 @@ var init_types = __esm({
|
|
|
826
840
|
userScope: z.object({
|
|
827
841
|
skills: z.string().optional(),
|
|
828
842
|
rules: z.string().optional(),
|
|
829
|
-
agents: z.string().optional()
|
|
843
|
+
agents: z.string().optional(),
|
|
844
|
+
hooks: z.string().optional()
|
|
830
845
|
}).optional()
|
|
831
846
|
});
|
|
832
847
|
ScopeEnum = z.enum(["user", "project"]);
|
|
@@ -954,6 +969,15 @@ var init_types = __esm({
|
|
|
954
969
|
tclaude: { skills: ".tclaude/skills", rules: ".tclaude/rules", settings: ".tclaude/settings.json", claudemd: ".tclaude/CLAUDE.md", agents: ".tclaude/agents", mcp: ".tclaude/.claude.json" },
|
|
955
970
|
tcodex: { skills: ".tcodex/skills", rules: ".tcodex/rules", settings: ".tcodex/hooks.json", agents: ".tcodex/agents" },
|
|
956
971
|
cursor: { skills: ".cursor/skills", rules: ".cursor/rules", settings: ".cursor/hooks.json", agents: ".cursor/agents", mcp: ".cursor/mcp.json", mcpProject: ".cursor/mcp.json" },
|
|
972
|
+
// GitHub Copilot CLI keeps project customizations under .github and moves
|
|
973
|
+
// the complete user customization root when COPILOT_HOME is set. Hooks are
|
|
974
|
+
// a standalone file; settings.json is deliberately never managed.
|
|
975
|
+
copilot: {
|
|
976
|
+
skills: ".github/skills",
|
|
977
|
+
rules: ".github/instructions",
|
|
978
|
+
hooks: ".github/hooks/teamai.json",
|
|
979
|
+
userScope: { skills: "skills", rules: "instructions", hooks: "hooks/teamai.json" }
|
|
980
|
+
},
|
|
957
981
|
// JoyCode: skills, rules (.mdc), and subagents are synced to .joycode/.
|
|
958
982
|
// JoyCode currently does not provide a lifecycle hooks system or startup
|
|
959
983
|
// adapter, so it intentionally has no `settings` path. Hook reconciliation
|
|
@@ -1238,6 +1262,8 @@ var init_types = __esm({
|
|
|
1238
1262
|
company: CultureCompanySchema.optional(),
|
|
1239
1263
|
team: CultureTeamSchema.optional()
|
|
1240
1264
|
});
|
|
1265
|
+
COPILOT_TOOL_ID = "copilot";
|
|
1266
|
+
DEFAULT_COPILOT_HOME = ".copilot";
|
|
1241
1267
|
REPORTS_BRANCH = "teamai-reports";
|
|
1242
1268
|
REPORTS_WORKTREE_DIRNAME = "reports-wt";
|
|
1243
1269
|
KNOWLEDGE_WORKTREE_DIRNAME = "knowledge-wt";
|
|
@@ -1587,7 +1613,7 @@ function getWrapperDispatchCommand(event, tool, matcher) {
|
|
|
1587
1613
|
return `PATH="$HOME/${TEAMAI_BIN_DIR}:$PATH" teamai hook-dispatch ${event} --tool ${tool}${matcherArg} 2>/dev/null || true`;
|
|
1588
1614
|
}
|
|
1589
1615
|
function builtinHookDefs(tool) {
|
|
1590
|
-
const withTimeout3 = tool === "cursor" || tool === "workbuddy" || tool === "codebuddy";
|
|
1616
|
+
const withTimeout3 = tool === "cursor" || tool === "copilot" || tool === "workbuddy" || tool === "codebuddy";
|
|
1591
1617
|
const buildCommand = tool === "zcode" ? getRawDispatchCommand : WRAPPER_TOOLS.has(tool) ? getWrapperDispatchCommand : getDispatchCommand;
|
|
1592
1618
|
return BUILTIN_HOOK_SPECS.map((spec) => ({
|
|
1593
1619
|
source: "builtin",
|
|
@@ -1639,10 +1665,18 @@ function toolInstallRoot(toolPath) {
|
|
|
1639
1665
|
}
|
|
1640
1666
|
return segments[0] ?? toolPath;
|
|
1641
1667
|
}
|
|
1668
|
+
async function isToolInstalledForConfig(tool, toolPath, localConfig) {
|
|
1669
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
1670
|
+
if (tool === COPILOT_TOOL_ID) {
|
|
1671
|
+
return localConfig.enabledAgents?.includes(COPILOT_TOOL_ID) === true || pathExists(getCopilotHome());
|
|
1672
|
+
}
|
|
1673
|
+
return ResourceHandler.isToolInstalled(toolPath, baseDir);
|
|
1674
|
+
}
|
|
1642
1675
|
var TOMBSTONE_FILE, ResourceHandler;
|
|
1643
1676
|
var init_base = __esm({
|
|
1644
1677
|
"src/resources/base.ts"() {
|
|
1645
1678
|
"use strict";
|
|
1679
|
+
init_types();
|
|
1646
1680
|
init_fs();
|
|
1647
1681
|
init_home();
|
|
1648
1682
|
TOMBSTONE_FILE = ".removed";
|
|
@@ -2509,6 +2543,7 @@ var init_hermes_hooks = __esm({
|
|
|
2509
2543
|
var hooks_exports = {};
|
|
2510
2544
|
__export(hooks_exports, {
|
|
2511
2545
|
AGENT_HOOK_EVENTS: () => AGENT_HOOK_EVENTS,
|
|
2546
|
+
CLAUDE_TO_COPILOT_EVENTS: () => CLAUDE_TO_COPILOT_EVENTS,
|
|
2512
2547
|
CLAUDE_TO_CURSOR_EVENTS: () => CLAUDE_TO_CURSOR_EVENTS,
|
|
2513
2548
|
OPENCLAW_TOOLS: () => OPENCLAW_TOOLS,
|
|
2514
2549
|
TEAMAI_HOOK_SUBCOMMANDS: () => TEAMAI_HOOK_SUBCOMMANDS,
|
|
@@ -2534,6 +2569,7 @@ __export(hooks_exports, {
|
|
|
2534
2569
|
import path14 from "path";
|
|
2535
2570
|
import { realpathSync } from "fs";
|
|
2536
2571
|
function detectFormat(tool) {
|
|
2572
|
+
if (tool === COPILOT_TOOL_ID) return "copilot";
|
|
2537
2573
|
if (CODEX_TOOLS.has(tool)) return "codex";
|
|
2538
2574
|
if (ZCODE_TOOLS.has(tool)) return "zcode";
|
|
2539
2575
|
return CURSOR_TOOLS.has(tool) ? "cursor" : "claude";
|
|
@@ -2623,6 +2659,20 @@ function toCodexEntry(def) {
|
|
|
2623
2659
|
if (def.matcher && def.matcher !== "*") entry.matcher = def.matcher;
|
|
2624
2660
|
return entry;
|
|
2625
2661
|
}
|
|
2662
|
+
function copilotPowershellCommand(command) {
|
|
2663
|
+
const match = command.match(COPILOT_BUILTIN_COMMAND_RE);
|
|
2664
|
+
return match ? `${match[1]} 2>$null; exit 0` : command;
|
|
2665
|
+
}
|
|
2666
|
+
function toCopilotEntry(def) {
|
|
2667
|
+
return {
|
|
2668
|
+
type: "command",
|
|
2669
|
+
bash: def.command,
|
|
2670
|
+
powershell: copilotPowershellCommand(def.command),
|
|
2671
|
+
command: def.command,
|
|
2672
|
+
...def.matcher && def.matcher !== "*" ? { matcher: def.matcher } : {},
|
|
2673
|
+
...def.timeout !== void 0 ? { timeoutSec: def.timeout } : {}
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2626
2676
|
function toZcodeEntry(def) {
|
|
2627
2677
|
const ZCODE_TIMEOUT_MS = {
|
|
2628
2678
|
SessionStart: 18e4,
|
|
@@ -2768,6 +2818,61 @@ async function reconcileCursorFormat(hooksPath, tool, teamDefs, opts, priorTeamC
|
|
|
2768
2818
|
log.debug(`teamai hooks already up-to-date in ${hooksPath}`);
|
|
2769
2819
|
}
|
|
2770
2820
|
}
|
|
2821
|
+
function copilotEntryCommands(entry) {
|
|
2822
|
+
return [entry.bash, entry.powershell, entry.command].filter(Boolean);
|
|
2823
|
+
}
|
|
2824
|
+
async function reconcileCopilotFormat(hooksPath, tool, teamDefs, opts, priorTeamCommands) {
|
|
2825
|
+
const expanded = expandHome(hooksPath);
|
|
2826
|
+
const existed = await pathExists(expanded);
|
|
2827
|
+
if (opts.removeAll && !existed) {
|
|
2828
|
+
log.debug(`No teamai hooks to remove from ${hooksPath}`);
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
await ensureDir(path14.dirname(expanded));
|
|
2832
|
+
const hooksJson = await readJson(expanded) ?? {
|
|
2833
|
+
version: COPILOT_HOOK_SCHEMA_VERSION,
|
|
2834
|
+
hooks: {}
|
|
2835
|
+
};
|
|
2836
|
+
let changed = hooksJson.version !== COPILOT_HOOK_SCHEMA_VERSION;
|
|
2837
|
+
hooksJson.version = COPILOT_HOOK_SCHEMA_VERSION;
|
|
2838
|
+
if (!hooksJson.hooks) hooksJson.hooks = {};
|
|
2839
|
+
const isManaged = (entry) => copilotEntryCommands(entry).some(
|
|
2840
|
+
(command) => TEAMAI_COMMAND_MARKERS.some((marker) => command.includes(marker)) || priorTeamCommands.has(command)
|
|
2841
|
+
);
|
|
2842
|
+
const defs = opts.removeAll ? [] : desiredDefs(tool, teamDefs, opts.builtinOverride);
|
|
2843
|
+
const desiredByEvent = {};
|
|
2844
|
+
for (const def of defs) {
|
|
2845
|
+
const event = CLAUDE_TO_COPILOT_EVENTS[def.event];
|
|
2846
|
+
if (!event) continue;
|
|
2847
|
+
(desiredByEvent[event] ??= []).push(toCopilotEntry(def));
|
|
2848
|
+
}
|
|
2849
|
+
for (const event of Object.keys(hooksJson.hooks)) {
|
|
2850
|
+
const existing = hooksJson.hooks[event] ?? [];
|
|
2851
|
+
const untouched = existing.filter((entry) => !isManaged(entry));
|
|
2852
|
+
const desired = desiredByEvent[event] ?? [];
|
|
2853
|
+
const next = [...untouched, ...desired];
|
|
2854
|
+
if (next.length === 0 && !opts.removeAll) {
|
|
2855
|
+
if (existing.length > 0) changed = true;
|
|
2856
|
+
delete hooksJson.hooks[event];
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
if (JSON.stringify(existing) !== JSON.stringify(next)) {
|
|
2860
|
+
hooksJson.hooks[event] = next;
|
|
2861
|
+
changed = true;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
for (const event of desiredEventOrder(defs, (value) => CLAUDE_TO_COPILOT_EVENTS[value])) {
|
|
2865
|
+
if (hooksJson.hooks[event]) continue;
|
|
2866
|
+
hooksJson.hooks[event] = desiredByEvent[event];
|
|
2867
|
+
changed = true;
|
|
2868
|
+
}
|
|
2869
|
+
if (changed || !existed) {
|
|
2870
|
+
await writeJson(expanded, hooksJson);
|
|
2871
|
+
log.success(`${opts.removeAll ? "Removed" : "Updated"} teamai hooks in ${hooksPath}`);
|
|
2872
|
+
} else {
|
|
2873
|
+
log.debug(`teamai hooks already up-to-date in ${hooksPath}`);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2771
2876
|
async function reconcileCodexFormat(hooksPath, tool, teamDefs, opts, priorTeamCommands) {
|
|
2772
2877
|
const expanded = expandHome(hooksPath);
|
|
2773
2878
|
await ensureDir(path14.dirname(expanded));
|
|
@@ -2954,6 +3059,8 @@ async function reconcileHooks(settingsPath, tool, teamDefs = [], opts = {}) {
|
|
|
2954
3059
|
const format = detectFormat(tool);
|
|
2955
3060
|
if (format === "cursor") {
|
|
2956
3061
|
await reconcileCursorFormat(settingsPath, tool, scopedDefs, opts, priorTeamCommands);
|
|
3062
|
+
} else if (format === "copilot") {
|
|
3063
|
+
await reconcileCopilotFormat(settingsPath, tool, scopedDefs, opts, priorTeamCommands);
|
|
2957
3064
|
} else if (format === "codex") {
|
|
2958
3065
|
await reconcileCodexFormat(settingsPath, tool, scopedDefs, opts, priorTeamCommands);
|
|
2959
3066
|
} else if (format === "zcode") {
|
|
@@ -3007,6 +3114,19 @@ async function getHookStatus(settingsPath, tool) {
|
|
|
3007
3114
|
});
|
|
3008
3115
|
return present2 ? "installed" : "missing";
|
|
3009
3116
|
}
|
|
3117
|
+
if (format === "copilot") {
|
|
3118
|
+
const hooksJson = await readJson(expanded);
|
|
3119
|
+
if (hooksJson?.version !== COPILOT_HOOK_SCHEMA_VERSION || !hooksJson.hooks) return "missing";
|
|
3120
|
+
const present2 = defs.every((def) => {
|
|
3121
|
+
const event = CLAUDE_TO_COPILOT_EVENTS[def.event];
|
|
3122
|
+
if (!event) return true;
|
|
3123
|
+
const want = toCopilotEntry(def);
|
|
3124
|
+
return (hooksJson.hooks[event] ?? []).some(
|
|
3125
|
+
(entry) => entry.type === want.type && entry.bash === want.bash && entry.powershell === want.powershell && entry.command === want.command && entry.matcher === want.matcher
|
|
3126
|
+
);
|
|
3127
|
+
});
|
|
3128
|
+
return present2 ? "installed" : "missing";
|
|
3129
|
+
}
|
|
3010
3130
|
if (format === "codex") {
|
|
3011
3131
|
const hooksJson = await readJson(expanded);
|
|
3012
3132
|
if (!hooksJson?.hooks) return "missing";
|
|
@@ -3050,6 +3170,15 @@ async function hasTeamaiHooks(settingsPath, tool, manifestPath) {
|
|
|
3050
3170
|
(entries) => (entries ?? []).some((e) => isTeamaiHookCommand(e.command) || priorTeamCommands.has(e.command))
|
|
3051
3171
|
);
|
|
3052
3172
|
}
|
|
3173
|
+
if (format === "copilot") {
|
|
3174
|
+
const j = await readJson(expanded);
|
|
3175
|
+
if (!j?.hooks) return false;
|
|
3176
|
+
return Object.values(j.hooks).some(
|
|
3177
|
+
(entries) => (entries ?? []).some((entry) => copilotEntryCommands(entry).some(
|
|
3178
|
+
(command) => TEAMAI_COMMAND_MARKERS.some((marker) => command.includes(marker)) || priorTeamCommands.has(command)
|
|
3179
|
+
))
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3053
3182
|
if (format === "codex") {
|
|
3054
3183
|
const j = await readJson(expanded);
|
|
3055
3184
|
if (!j?.hooks) return false;
|
|
@@ -3218,7 +3347,8 @@ async function reconcileTeamHooksForConfig(teamConfig, localConfig, opts = {}) {
|
|
|
3218
3347
|
activeRoles: activeRoleIds(localConfig)
|
|
3219
3348
|
});
|
|
3220
3349
|
const { baseDir, manifestPath } = resolveHookScope(localConfig);
|
|
3221
|
-
|
|
3350
|
+
const explicitlySelectedAgents = opts.filterAgents ?? localConfig.enabledAgents;
|
|
3351
|
+
let filterAgents2 = explicitlySelectedAgents;
|
|
3222
3352
|
const disabled = localConfig.disabledAgents;
|
|
3223
3353
|
if (disabled && disabled.length > 0) {
|
|
3224
3354
|
const universe = filterAgents2 ?? Object.keys(teamConfig.toolPaths);
|
|
@@ -3231,10 +3361,29 @@ async function reconcileTeamHooksForConfig(teamConfig, localConfig, opts = {}) {
|
|
|
3231
3361
|
teamHookProjectRoot: localConfig.scope === "project" && !isSelfMode(localConfig) ? localConfig.projectRoot : void 0,
|
|
3232
3362
|
installedBaseDir: localConfig.scope === "project" ? localConfig.projectRoot ?? baseDir : void 0
|
|
3233
3363
|
});
|
|
3364
|
+
const copilotExcluded = disabled?.includes(COPILOT_TOOL_ID) ?? false;
|
|
3365
|
+
const copilotSelected = !copilotExcluded && (explicitlySelectedAgents?.includes(COPILOT_TOOL_ID) ?? false);
|
|
3366
|
+
const copilotEnabled = !copilotExcluded && (copilotSelected || explicitlySelectedAgents === void 0 && await pathExists(getCopilotHome()));
|
|
3367
|
+
const copilotPaths = scopedToolPaths(teamConfig, localConfig)[COPILOT_TOOL_ID];
|
|
3368
|
+
if (copilotEnabled && copilotPaths?.hooks) {
|
|
3369
|
+
const copilotBase = resolveToolBaseDir(COPILOT_TOOL_ID, localConfig);
|
|
3370
|
+
if (copilotSelected || await pathExists(getCopilotHome())) {
|
|
3371
|
+
await reconcileHooks(
|
|
3372
|
+
path14.join(copilotBase, copilotPaths.hooks),
|
|
3373
|
+
COPILOT_TOOL_ID,
|
|
3374
|
+
teamDefs,
|
|
3375
|
+
{
|
|
3376
|
+
manifestPath: getManagedHooksPath(localConfig.scope, localConfig.projectRoot),
|
|
3377
|
+
removeAll: opts.removeAll,
|
|
3378
|
+
builtinOverride: builtin
|
|
3379
|
+
}
|
|
3380
|
+
);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3234
3383
|
await sweepLegacyProjectHooks(teamConfig.toolPaths, localConfig);
|
|
3235
3384
|
return teamDefs;
|
|
3236
3385
|
}
|
|
3237
|
-
var OPENCLAW_TOOLS, TEAMAI_HOOK_SUBCOMMANDS, TEAMAI_LEGACY_HOOK_SUBCOMMANDS, CLAUDE_TO_CURSOR_EVENTS, CURSOR_TOOLS, CODEX_TOOLS, ZCODE_TOOLS, CODEX_TRUST_GATE_TOOLS, TEAMAI_COMMAND_MARKERS, AGENT_HOOK_EVENTS;
|
|
3386
|
+
var OPENCLAW_TOOLS, TEAMAI_HOOK_SUBCOMMANDS, TEAMAI_LEGACY_HOOK_SUBCOMMANDS, CLAUDE_TO_CURSOR_EVENTS, CLAUDE_TO_COPILOT_EVENTS, COPILOT_HOOK_SCHEMA_VERSION, CURSOR_TOOLS, CODEX_TOOLS, ZCODE_TOOLS, CODEX_TRUST_GATE_TOOLS, TEAMAI_COMMAND_MARKERS, COPILOT_BUILTIN_COMMAND_RE, AGENT_HOOK_EVENTS;
|
|
3238
3387
|
var init_hooks2 = __esm({
|
|
3239
3388
|
"src/hooks.ts"() {
|
|
3240
3389
|
"use strict";
|
|
@@ -3255,6 +3404,14 @@ var init_hooks2 = __esm({
|
|
|
3255
3404
|
PostToolUse: "postToolUse",
|
|
3256
3405
|
UserPromptSubmit: "beforeSubmitPrompt"
|
|
3257
3406
|
};
|
|
3407
|
+
CLAUDE_TO_COPILOT_EVENTS = {
|
|
3408
|
+
SessionStart: "SessionStart",
|
|
3409
|
+
Stop: "Stop",
|
|
3410
|
+
UserPromptSubmit: "UserPromptSubmit",
|
|
3411
|
+
PreToolUse: "PreToolUse",
|
|
3412
|
+
PostToolUse: "PostToolUse"
|
|
3413
|
+
};
|
|
3414
|
+
COPILOT_HOOK_SCHEMA_VERSION = 1;
|
|
3258
3415
|
CURSOR_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
|
|
3259
3416
|
CODEX_TOOLS = /* @__PURE__ */ new Set(["codex", "codex-internal", "tcodex"]);
|
|
3260
3417
|
ZCODE_TOOLS = /* @__PURE__ */ new Set(["zcode"]);
|
|
@@ -3270,6 +3427,7 @@ var init_hooks2 = __esm({
|
|
|
3270
3427
|
"teamai mr-hint",
|
|
3271
3428
|
"teamai hook-dispatch"
|
|
3272
3429
|
];
|
|
3430
|
+
COPILOT_BUILTIN_COMMAND_RE = /^bash -lc "(teamai hook-dispatch [^"]+) 2>\/dev\/null" \|\| true$/;
|
|
3273
3431
|
AGENT_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
3274
3432
|
"SessionStart",
|
|
3275
3433
|
"UserPromptSubmit",
|
|
@@ -3375,12 +3533,17 @@ var init_pending_push = __esm({
|
|
|
3375
3533
|
|
|
3376
3534
|
// src/resources/rule-format.ts
|
|
3377
3535
|
function ruleFileExtensionForTool(tool) {
|
|
3378
|
-
|
|
3536
|
+
if (usesCursorMdcRules(tool)) return ".mdc";
|
|
3537
|
+
return usesCopilotInstructions(tool) ? ".instructions.md" : ".md";
|
|
3379
3538
|
}
|
|
3380
3539
|
function usesCursorMdcRules(tool) {
|
|
3381
3540
|
return CURSOR_MDC_RULE_TOOLS.has(tool);
|
|
3382
3541
|
}
|
|
3542
|
+
function usesCopilotInstructions(tool) {
|
|
3543
|
+
return COPILOT_INSTRUCTIONS_RULE_TOOLS.has(tool);
|
|
3544
|
+
}
|
|
3383
3545
|
function ruleStemFromFilename(filename) {
|
|
3546
|
+
if (filename.endsWith(".instructions.md")) return filename.slice(0, -".instructions.md".length);
|
|
3384
3547
|
if (filename.endsWith(".mdc")) return filename.slice(0, -".mdc".length);
|
|
3385
3548
|
if (filename.endsWith(".md")) return filename.slice(0, -".md".length);
|
|
3386
3549
|
return null;
|
|
@@ -3388,11 +3551,12 @@ function ruleStemFromFilename(filename) {
|
|
|
3388
3551
|
function isLegacyCursorRuleFile(tool, filename) {
|
|
3389
3552
|
return usesCursorMdcRules(tool) && filename.endsWith(".md");
|
|
3390
3553
|
}
|
|
3391
|
-
var CURSOR_MDC_RULE_TOOLS;
|
|
3554
|
+
var CURSOR_MDC_RULE_TOOLS, COPILOT_INSTRUCTIONS_RULE_TOOLS;
|
|
3392
3555
|
var init_rule_format = __esm({
|
|
3393
3556
|
"src/resources/rule-format.ts"() {
|
|
3394
3557
|
"use strict";
|
|
3395
3558
|
CURSOR_MDC_RULE_TOOLS = /* @__PURE__ */ new Set(["cursor", "joycode"]);
|
|
3559
|
+
COPILOT_INSTRUCTIONS_RULE_TOOLS = /* @__PURE__ */ new Set(["copilot"]);
|
|
3396
3560
|
}
|
|
3397
3561
|
});
|
|
3398
3562
|
|
|
@@ -8864,6 +9028,10 @@ function codexServerNames(source) {
|
|
|
8864
9028
|
for (const m of source.matchAll(/^\[mcp_servers\.([A-Za-z0-9_-]+)\]\s*$/gm)) names.add(m[1]);
|
|
8865
9029
|
return [...names];
|
|
8866
9030
|
}
|
|
9031
|
+
function mcpTargetExcluded(localConfig, target) {
|
|
9032
|
+
if (!isAgentExcluded(localConfig, target.tool)) return false;
|
|
9033
|
+
return !(target.projectScope && target.tool === "claude" && !isAgentExcluded(localConfig, "tclaude"));
|
|
9034
|
+
}
|
|
8867
9035
|
async function reconcileMcpForConfig(teamConfig, localConfig, options = {}) {
|
|
8868
9036
|
const changes = [];
|
|
8869
9037
|
let wrote = false;
|
|
@@ -8902,6 +9070,7 @@ async function reconcileMcpForConfig(teamConfig, localConfig, options = {}) {
|
|
|
8902
9070
|
if (teamDefs.length === 0 && nothingOwned) return { changes, wrote };
|
|
8903
9071
|
const vars = await buildVarTable(localConfig);
|
|
8904
9072
|
for (const target of targets) {
|
|
9073
|
+
if (!removeAll && mcpTargetExcluded(localConfig, target)) continue;
|
|
8905
9074
|
const manifestKey = managedMcpManifestKey(target.tool, target.projectScope);
|
|
8906
9075
|
const owned = manifest[manifestKey] ?? [];
|
|
8907
9076
|
const ownedNames = new Set(owned.map((r) => r.name));
|
|
@@ -12331,9 +12500,9 @@ var init_skills = __esm({
|
|
|
12331
12500
|
sourceSkillNames = /* @__PURE__ */ new Set();
|
|
12332
12501
|
}
|
|
12333
12502
|
const candidates = /* @__PURE__ */ new Map();
|
|
12334
|
-
for (const [
|
|
12503
|
+
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12335
12504
|
if (!toolPath.skills) continue;
|
|
12336
|
-
const skillsDir = path30.join(
|
|
12505
|
+
const skillsDir = path30.join(resolveToolBaseDir(tool, localConfig), toolPath.skills);
|
|
12337
12506
|
if (!await pathExists(skillsDir)) continue;
|
|
12338
12507
|
const localSkills = await scanSkillsRecursively(skillsDir);
|
|
12339
12508
|
for (const [dir, localDirPath] of localSkills) {
|
|
@@ -12441,7 +12610,6 @@ var init_skills = __esm({
|
|
|
12441
12610
|
* Pull a skill from team repo to all configured AI tool directories.
|
|
12442
12611
|
*/
|
|
12443
12612
|
async pullItem(item, teamConfig, localConfig) {
|
|
12444
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
12445
12613
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12446
12614
|
if (isAgentExcluded(localConfig, tool)) continue;
|
|
12447
12615
|
if (!toolPath.skills) continue;
|
|
@@ -12460,10 +12628,11 @@ var init_skills = __esm({
|
|
|
12460
12628
|
}
|
|
12461
12629
|
dest = path30.join(getHermesHome(), "skills", item.name);
|
|
12462
12630
|
} else {
|
|
12463
|
-
if (!await
|
|
12631
|
+
if (!await isToolInstalledForConfig(tool, toolPath.skills, localConfig)) {
|
|
12464
12632
|
log.debug(`Skipping skill sync for ${tool}: tool not installed`);
|
|
12465
12633
|
continue;
|
|
12466
12634
|
}
|
|
12635
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
12467
12636
|
dest = await resolveSkillDestination(tool, toolPath.skills, baseDir, item.name, item.sourcePath);
|
|
12468
12637
|
}
|
|
12469
12638
|
try {
|
|
@@ -12480,7 +12649,6 @@ var init_skills = __esm({
|
|
|
12480
12649
|
*/
|
|
12481
12650
|
async removeItem(name, teamConfig, localConfig) {
|
|
12482
12651
|
const removed = [];
|
|
12483
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
12484
12652
|
const scopedNamespaces = await resolveSkillNamespaces(localConfig);
|
|
12485
12653
|
if (scopedNamespaces.length > 0) {
|
|
12486
12654
|
for (const namespace of scopedNamespaces) {
|
|
@@ -12507,6 +12675,7 @@ var init_skills = __esm({
|
|
|
12507
12675
|
if (!wsDir) continue;
|
|
12508
12676
|
skillDir = path30.join(wsDir, "skills", name);
|
|
12509
12677
|
} else {
|
|
12678
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
12510
12679
|
const configuredDir = path30.join(baseDir, toolPath.skills, name);
|
|
12511
12680
|
skillDir = await resolveSkillDestination(tool, toolPath.skills, baseDir, name);
|
|
12512
12681
|
if (skillDir !== configuredDir && await pathExists(configuredDir) && await dirContentEqual(skillDir, configuredDir)) {
|
|
@@ -12535,6 +12704,55 @@ var init_skills = __esm({
|
|
|
12535
12704
|
}
|
|
12536
12705
|
});
|
|
12537
12706
|
|
|
12707
|
+
// src/resources/copilot-instructions.ts
|
|
12708
|
+
function normalizeBody2(body) {
|
|
12709
|
+
return body.replace(/^\s+/, "").replace(/\s+$/, "");
|
|
12710
|
+
}
|
|
12711
|
+
function rulePaths(data) {
|
|
12712
|
+
const value = data.paths;
|
|
12713
|
+
if (Array.isArray(value)) {
|
|
12714
|
+
return value.map((entry) => String(entry).trim()).filter(Boolean);
|
|
12715
|
+
}
|
|
12716
|
+
if (typeof value === "string") {
|
|
12717
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
12718
|
+
}
|
|
12719
|
+
return [];
|
|
12720
|
+
}
|
|
12721
|
+
function teamRuleToCopilotInstructions(rawTeamRule) {
|
|
12722
|
+
const { data, body } = splitFrontmatter2(rawTeamRule);
|
|
12723
|
+
const paths = rulePaths(data);
|
|
12724
|
+
return stringifyFrontmatter(
|
|
12725
|
+
{ applyTo: paths.length > 0 ? paths.join(", ") : ALL_FILES_GLOB },
|
|
12726
|
+
`
|
|
12727
|
+
${normalizeBody2(body)}
|
|
12728
|
+
`
|
|
12729
|
+
);
|
|
12730
|
+
}
|
|
12731
|
+
function mergeCopilotBodyIntoTeamMd(rawCopilotInstructions, existingTeamMd) {
|
|
12732
|
+
const body = normalizeBody2(splitFrontmatter2(rawCopilotInstructions).body);
|
|
12733
|
+
if (existingTeamMd === null) return `${body}
|
|
12734
|
+
`;
|
|
12735
|
+
const existing = splitFrontmatter2(existingTeamMd);
|
|
12736
|
+
if (normalizeBody2(existing.body) === body) return existingTeamMd;
|
|
12737
|
+
if (!existing.raw) return `${body}
|
|
12738
|
+
`;
|
|
12739
|
+
return `${existing.raw.endsWith("\n") ? existing.raw : `${existing.raw}
|
|
12740
|
+
`}
|
|
12741
|
+
${body}
|
|
12742
|
+
`;
|
|
12743
|
+
}
|
|
12744
|
+
function copilotInstructionsBodyEqualsTeamMd(rawCopilotInstructions, rawTeamRule) {
|
|
12745
|
+
return normalizeBody2(splitFrontmatter2(rawCopilotInstructions).body) === normalizeBody2(splitFrontmatter2(rawTeamRule).body);
|
|
12746
|
+
}
|
|
12747
|
+
var ALL_FILES_GLOB;
|
|
12748
|
+
var init_copilot_instructions = __esm({
|
|
12749
|
+
"src/resources/copilot-instructions.ts"() {
|
|
12750
|
+
"use strict";
|
|
12751
|
+
init_frontmatter();
|
|
12752
|
+
ALL_FILES_GLOB = "**";
|
|
12753
|
+
}
|
|
12754
|
+
});
|
|
12755
|
+
|
|
12538
12756
|
// src/resources/opencode-config.ts
|
|
12539
12757
|
var opencode_config_exports = {};
|
|
12540
12758
|
__export(opencode_config_exports, {
|
|
@@ -12607,6 +12825,7 @@ var init_rules = __esm({
|
|
|
12607
12825
|
init_types();
|
|
12608
12826
|
init_builtin_rules();
|
|
12609
12827
|
init_cursor_mdc();
|
|
12828
|
+
init_copilot_instructions();
|
|
12610
12829
|
init_rule_format();
|
|
12611
12830
|
RulesHandler = class extends ResourceHandler {
|
|
12612
12831
|
type = "rules";
|
|
@@ -12634,10 +12853,11 @@ var init_rules = __esm({
|
|
|
12634
12853
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12635
12854
|
const rulesPath = toolPath.rules;
|
|
12636
12855
|
if (!rulesPath) continue;
|
|
12637
|
-
const rulesDir = path32.join(
|
|
12856
|
+
const rulesDir = path32.join(resolveToolBaseDir(tool, localConfig), rulesPath);
|
|
12638
12857
|
if (!await pathExists(rulesDir)) continue;
|
|
12639
12858
|
const ext = ruleFileExtensionForTool(tool);
|
|
12640
12859
|
const isMdcTool = usesCursorMdcRules(tool);
|
|
12860
|
+
const isCopilotTool = usesCopilotInstructions(tool);
|
|
12641
12861
|
const files = await listFilesRecursive(rulesDir);
|
|
12642
12862
|
for (const file of files) {
|
|
12643
12863
|
if (!file.endsWith(ext)) continue;
|
|
@@ -12648,10 +12868,12 @@ var init_rules = __esm({
|
|
|
12648
12868
|
const teamFileName = `${name}.md`;
|
|
12649
12869
|
if (teamRules.has(teamFileName)) {
|
|
12650
12870
|
const teamFilePath = path32.join(teamRulesDir, teamFileName);
|
|
12871
|
+
const localRule = await readFileSafe(localFilePath) ?? "";
|
|
12872
|
+
const teamRule = await readTeamRule(teamFilePath);
|
|
12651
12873
|
const equal = isMdcTool ? cursorMdcBodyEqualsTeamMd(
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
) : await fileContentEqual(localFilePath, teamFilePath);
|
|
12874
|
+
localRule,
|
|
12875
|
+
teamRule
|
|
12876
|
+
) : isCopilotTool ? copilotInstructionsBodyEqualsTeamMd(localRule, teamRule) : await fileContentEqual(localFilePath, teamFilePath);
|
|
12655
12877
|
if (equal) continue;
|
|
12656
12878
|
const mtime = await getFileMtime(localFilePath);
|
|
12657
12879
|
const existing = candidates.get(name);
|
|
@@ -12659,7 +12881,7 @@ var init_rules = __esm({
|
|
|
12659
12881
|
candidates.set(name, { sourcePath: localFilePath, mtime, status: "modified" });
|
|
12660
12882
|
}
|
|
12661
12883
|
} else {
|
|
12662
|
-
if (isMdcTool) continue;
|
|
12884
|
+
if (isMdcTool || isCopilotTool) continue;
|
|
12663
12885
|
const existing = candidates.get(name);
|
|
12664
12886
|
if (!existing) {
|
|
12665
12887
|
const mtime = await getFileMtime(localFilePath);
|
|
@@ -12705,6 +12927,12 @@ var init_rules = __esm({
|
|
|
12705
12927
|
throw new Error(`Cannot read rule source ${item.sourcePath}`);
|
|
12706
12928
|
}
|
|
12707
12929
|
await writeFile(dest, mergeCursorBodyIntoTeamMd(raw, await readFileSafe(dest)));
|
|
12930
|
+
} else if (item.sourcePath.endsWith(".instructions.md")) {
|
|
12931
|
+
const raw = await readFileSafe(item.sourcePath);
|
|
12932
|
+
if (raw === null) {
|
|
12933
|
+
throw new Error(`Cannot read rule source ${item.sourcePath}`);
|
|
12934
|
+
}
|
|
12935
|
+
await writeFile(dest, mergeCopilotBodyIntoTeamMd(raw, await readFileSafe(dest)));
|
|
12708
12936
|
} else {
|
|
12709
12937
|
await copyFile(item.sourcePath, dest);
|
|
12710
12938
|
}
|
|
@@ -12715,15 +12943,14 @@ var init_rules = __esm({
|
|
|
12715
12943
|
* Pull a single rule file to all configured AI tool rules/ directories.
|
|
12716
12944
|
*/
|
|
12717
12945
|
async pullItem(item, teamConfig, localConfig) {
|
|
12718
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
12719
12946
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12720
12947
|
if (isAgentExcluded(localConfig, tool)) continue;
|
|
12721
12948
|
if (!toolPath.rules) continue;
|
|
12722
|
-
if (!await
|
|
12949
|
+
if (!await isToolInstalledForConfig(tool, toolPath.rules, localConfig)) {
|
|
12723
12950
|
log.debug(`Skipping rule sync for ${tool}: tool not installed`);
|
|
12724
12951
|
continue;
|
|
12725
12952
|
}
|
|
12726
|
-
const destDir = path32.join(
|
|
12953
|
+
const destDir = path32.join(resolveToolBaseDir(tool, localConfig), toolPath.rules);
|
|
12727
12954
|
await ensureDir(destDir);
|
|
12728
12955
|
const dest = path32.join(destDir, `${item.name}${ruleFileExtensionForTool(tool)}`);
|
|
12729
12956
|
try {
|
|
@@ -12734,6 +12961,13 @@ var init_rules = __esm({
|
|
|
12734
12961
|
}
|
|
12735
12962
|
await writeFile(dest, teamRuleToCursorMdc(raw));
|
|
12736
12963
|
await remove(path32.join(destDir, `${item.name}.md`));
|
|
12964
|
+
} else if (usesCopilotInstructions(tool)) {
|
|
12965
|
+
const raw = await readFileSafe(item.sourcePath);
|
|
12966
|
+
if (raw === null) {
|
|
12967
|
+
throw new Error(`Cannot read rule source ${item.sourcePath}`);
|
|
12968
|
+
}
|
|
12969
|
+
await writeFile(dest, teamRuleToCopilotInstructions(raw));
|
|
12970
|
+
await remove(path32.join(destDir, `${item.name}.md`));
|
|
12737
12971
|
} else {
|
|
12738
12972
|
await copyFile(item.sourcePath, dest);
|
|
12739
12973
|
}
|
|
@@ -12748,7 +12982,6 @@ var init_rules = __esm({
|
|
|
12748
12982
|
*/
|
|
12749
12983
|
async removeItem(name, teamConfig, localConfig) {
|
|
12750
12984
|
const removed = [];
|
|
12751
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
12752
12985
|
const teamFile = path32.join(localConfig.repo.localPath, "rules", `${name}.md`);
|
|
12753
12986
|
if (await pathExists(teamFile)) {
|
|
12754
12987
|
await remove(teamFile);
|
|
@@ -12758,6 +12991,7 @@ var init_rules = __esm({
|
|
|
12758
12991
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12759
12992
|
if (!toolPath.rules) continue;
|
|
12760
12993
|
if (isAgentExcluded(localConfig, tool)) continue;
|
|
12994
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
12761
12995
|
const extensions = /* @__PURE__ */ new Set([ruleFileExtensionForTool(tool), ".md"]);
|
|
12762
12996
|
for (const extension of extensions) {
|
|
12763
12997
|
const filePath = path32.join(baseDir, toolPath.rules, `${name}${extension}`);
|
|
@@ -12796,11 +13030,11 @@ var init_rules = __esm({
|
|
|
12796
13030
|
}
|
|
12797
13031
|
const teamRuleNames = new Set(rules.map((r) => r.name));
|
|
12798
13032
|
const tombstones = await this.readTombstones(localConfig);
|
|
12799
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
12800
13033
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12801
13034
|
if (!toolPath.rules) continue;
|
|
12802
13035
|
if (isAgentExcluded(localConfig, tool)) continue;
|
|
12803
|
-
if (!await
|
|
13036
|
+
if (!await isToolInstalledForConfig(tool, toolPath.rules, localConfig)) continue;
|
|
13037
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
12804
13038
|
const destDir = path32.join(baseDir, toolPath.rules);
|
|
12805
13039
|
if (!await pathExists(destDir)) continue;
|
|
12806
13040
|
const ext = ruleFileExtensionForTool(tool);
|
|
@@ -12808,7 +13042,7 @@ var init_rules = __esm({
|
|
|
12808
13042
|
for (const localFile of localFiles) {
|
|
12809
13043
|
const ruleName = ruleStemFromFilename(localFile);
|
|
12810
13044
|
if (ruleName === null) continue;
|
|
12811
|
-
if (tool === "joycode" && !tombstones.has(ruleName)) continue;
|
|
13045
|
+
if ((tool === "joycode" || usesCopilotInstructions(tool)) && !tombstones.has(ruleName)) continue;
|
|
12812
13046
|
if (isLegacyCursorRuleFile(tool, localFile)) {
|
|
12813
13047
|
await remove(path32.join(destDir, localFile));
|
|
12814
13048
|
log.debug(`Removed legacy .md rule ${localFile} from ${tool}`);
|
|
@@ -12824,8 +13058,9 @@ var init_rules = __esm({
|
|
|
12824
13058
|
}
|
|
12825
13059
|
await this.removeEmptyDirs(destDir);
|
|
12826
13060
|
}
|
|
12827
|
-
for (const [, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
13061
|
+
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
12828
13062
|
if (!toolPath.claudemd) continue;
|
|
13063
|
+
const baseDir = resolveToolBaseDir(tool, localConfig);
|
|
12829
13064
|
const claudeMdPath = path32.join(baseDir, toolPath.claudemd);
|
|
12830
13065
|
try {
|
|
12831
13066
|
const content = await readFileSafe(claudeMdPath);
|
|
@@ -15869,6 +16104,10 @@ async function detectHomeInstalledAgents(candidateIds = SELF_MODE_AGENT_CHOICES)
|
|
|
15869
16104
|
const home = getUserHome();
|
|
15870
16105
|
const found = [];
|
|
15871
16106
|
for (const id of candidateIds) {
|
|
16107
|
+
if (id === COPILOT_TOOL_ID) {
|
|
16108
|
+
if (await pathExists(getCopilotHome())) found.push(id);
|
|
16109
|
+
continue;
|
|
16110
|
+
}
|
|
15872
16111
|
const skillsPath = KNOWN_AGENTS.find((a) => a.id === id)?.skillsPath;
|
|
15873
16112
|
if (!skillsPath) continue;
|
|
15874
16113
|
const rootSegment = skillsPath.split("/")[0];
|
|
@@ -15903,7 +16142,6 @@ function getEffectiveAgents(teamConfig, localConfig) {
|
|
|
15903
16142
|
return [...byId.values()];
|
|
15904
16143
|
}
|
|
15905
16144
|
async function detectInstalledAgents(localConfig, teamConfig) {
|
|
15906
|
-
const baseDir = resolveBaseDir(localConfig);
|
|
15907
16145
|
const agents = getEffectiveAgents(teamConfig, localConfig);
|
|
15908
16146
|
const scoped = scopedToolPaths(teamConfig, localConfig);
|
|
15909
16147
|
const fromTeamConfig = new Set(
|
|
@@ -15911,12 +16149,11 @@ async function detectInstalledAgents(localConfig, teamConfig) {
|
|
|
15911
16149
|
);
|
|
15912
16150
|
const results = [];
|
|
15913
16151
|
for (const agent of agents) {
|
|
15914
|
-
const
|
|
15915
|
-
const
|
|
15916
|
-
const installed = rootSegment ? await pathExists(rootPath) : false;
|
|
16152
|
+
const baseDir = resolveToolBaseDir(agent.id, localConfig);
|
|
16153
|
+
const installed = await isToolInstalledForConfig(agent.id, agent.skillsPath, localConfig);
|
|
15917
16154
|
results.push({
|
|
15918
16155
|
...agent,
|
|
15919
|
-
absoluteSkillsPath:
|
|
16156
|
+
absoluteSkillsPath: path44.join(baseDir, agent.skillsPath),
|
|
15920
16157
|
installed,
|
|
15921
16158
|
fromTeamConfig: fromTeamConfig.has(agent.id)
|
|
15922
16159
|
});
|
|
@@ -15931,7 +16168,7 @@ var init_known_agents = __esm({
|
|
|
15931
16168
|
init_types();
|
|
15932
16169
|
init_base();
|
|
15933
16170
|
init_home();
|
|
15934
|
-
SELF_MODE_AGENT_CHOICES = ["claude", "codex", "cursor", "joycode", "codebuddy", "workbuddy"];
|
|
16171
|
+
SELF_MODE_AGENT_CHOICES = ["claude", "codex", "cursor", "copilot", "joycode", "codebuddy", "workbuddy"];
|
|
15935
16172
|
KNOWN_AGENTS = [
|
|
15936
16173
|
// Coding agents already wired through teamConfig.toolPaths defaults
|
|
15937
16174
|
{ id: "claude", displayName: "Claude Code", category: "coding", skillsPath: ".claude/skills" },
|
|
@@ -25055,10 +25292,12 @@ async function removeCore(type, names, options, localConfig, teamConfig) {
|
|
|
25055
25292
|
log.info("Dry run \u2014 no changes made");
|
|
25056
25293
|
return;
|
|
25057
25294
|
}
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25295
|
+
if (!options.force) {
|
|
25296
|
+
const confirmed = await askConfirmation("Are you sure? [y/N] ");
|
|
25297
|
+
if (!confirmed) {
|
|
25298
|
+
log.info("Cancelled");
|
|
25299
|
+
return;
|
|
25300
|
+
}
|
|
25062
25301
|
}
|
|
25063
25302
|
const spin = spinner(`Removing ${found.length} ${type}...`).start();
|
|
25064
25303
|
let totalRemoved = 0;
|
|
@@ -25140,16 +25379,20 @@ var init_remove = __esm({
|
|
|
25140
25379
|
// src/doctor.ts
|
|
25141
25380
|
var doctor_exports = {};
|
|
25142
25381
|
__export(doctor_exports, {
|
|
25143
|
-
|
|
25382
|
+
buildChecks: () => buildChecks,
|
|
25383
|
+
doctor: () => doctor,
|
|
25384
|
+
resolveDoctorContext: () => resolveDoctorContext
|
|
25144
25385
|
});
|
|
25145
25386
|
import path70 from "path";
|
|
25146
|
-
async function buildHookChecks(toolPaths, baseDir) {
|
|
25387
|
+
async function buildHookChecks(toolPaths, baseDir, localConfig) {
|
|
25147
25388
|
const checks = [];
|
|
25148
25389
|
for (const [tool, paths] of Object.entries(toolPaths)) {
|
|
25149
|
-
|
|
25150
|
-
|
|
25390
|
+
const hookPath = paths.hooks ? path70.join(resolveToolBaseDir(tool, localConfig), paths.hooks) : paths.settings ? path70.join(baseDir, paths.settings) : void 0;
|
|
25391
|
+
if (!hookPath) continue;
|
|
25392
|
+
const settingsPath = hookPath;
|
|
25151
25393
|
const parentDir = path70.dirname(settingsPath);
|
|
25152
|
-
|
|
25394
|
+
const installed = tool === COPILOT_TOOL_ID ? await isToolInstalledForConfig(tool, paths.hooks ?? paths.settings ?? "", localConfig) : await pathExists(parentDir);
|
|
25395
|
+
if (!installed) continue;
|
|
25153
25396
|
checks.push({
|
|
25154
25397
|
name: `teamai hooks in ${tool} settings`,
|
|
25155
25398
|
check: async () => {
|
|
@@ -25176,28 +25419,20 @@ async function hasInstalledCodexHooks(toolPaths, baseDir) {
|
|
|
25176
25419
|
}
|
|
25177
25420
|
return false;
|
|
25178
25421
|
}
|
|
25179
|
-
async function
|
|
25180
|
-
log.info("Running diagnostics...\n");
|
|
25422
|
+
async function resolveDoctorContext() {
|
|
25181
25423
|
const projectConfig = await detectProjectConfig();
|
|
25182
25424
|
const localConfig = projectConfig ?? await loadLocalConfig();
|
|
25183
|
-
if (!localConfig)
|
|
25184
|
-
console.log(" Scope: not initialized\n");
|
|
25185
|
-
console.log(" \u2716 TeamAI is not initialized");
|
|
25186
|
-
console.log(" \u2192 Run `teamai init <repo-url>` in a project, or add `--scope user` for all projects");
|
|
25187
|
-
console.log("");
|
|
25188
|
-
log.warn("Initialization is required before diagnostics can run.");
|
|
25189
|
-
return false;
|
|
25190
|
-
}
|
|
25191
|
-
const scope = localConfig.scope ?? "user";
|
|
25192
|
-
const scopeLabel = `${scope}${scope === "project" && localConfig.projectRoot ? ` (${localConfig.projectRoot})` : ""}`;
|
|
25193
|
-
console.log(` Scope: ${scopeLabel}
|
|
25194
|
-
`);
|
|
25425
|
+
if (!localConfig) return null;
|
|
25195
25426
|
const teamConfig = await loadTeamConfig(localConfig.repo.localPath);
|
|
25196
25427
|
const toolPaths = teamConfig ? Object.fromEntries(
|
|
25197
25428
|
Object.entries(scopedToolPaths(teamConfig, localConfig)).filter(([tool]) => !isAgentExcluded(localConfig, tool))
|
|
25198
25429
|
) : {};
|
|
25199
|
-
const providerName = teamConfig?.provider;
|
|
25200
25430
|
const baseDir = resolveHookScope(localConfig).baseDir;
|
|
25431
|
+
return { localConfig, teamConfig, toolPaths, baseDir };
|
|
25432
|
+
}
|
|
25433
|
+
async function buildChecks(ctx) {
|
|
25434
|
+
const { localConfig, teamConfig, toolPaths, baseDir } = ctx;
|
|
25435
|
+
const providerName = teamConfig?.provider;
|
|
25201
25436
|
const checks = [];
|
|
25202
25437
|
if (providerName === "tgit") {
|
|
25203
25438
|
const { isGfInstalled: isGfInstalled2, gfIsAuthenticated: gfIsAuthenticated2 } = await Promise.resolve().then(() => (init_tgit(), tgit_exports));
|
|
@@ -25256,7 +25491,7 @@ async function doctor(options) {
|
|
|
25256
25491
|
},
|
|
25257
25492
|
fix: "Check teamai.yaml in team repo for syntax errors"
|
|
25258
25493
|
},
|
|
25259
|
-
...await buildHookChecks(toolPaths, baseDir),
|
|
25494
|
+
...await buildHookChecks(toolPaths, baseDir, localConfig),
|
|
25260
25495
|
{
|
|
25261
25496
|
name: "Env variables injected in shell profile",
|
|
25262
25497
|
check: async () => {
|
|
@@ -25278,26 +25513,80 @@ async function doctor(options) {
|
|
|
25278
25513
|
fix: "Run `teamai pull` to inject env variables into shell profile"
|
|
25279
25514
|
}
|
|
25280
25515
|
);
|
|
25281
|
-
|
|
25516
|
+
return checks;
|
|
25517
|
+
}
|
|
25518
|
+
async function runChecks(checks, onResult) {
|
|
25519
|
+
const results = [];
|
|
25282
25520
|
for (const { name, check, fix } of checks) {
|
|
25283
25521
|
const ok = await check();
|
|
25284
|
-
|
|
25285
|
-
|
|
25522
|
+
const result = ok ? { name, ok } : { name, ok, fix };
|
|
25523
|
+
results.push(result);
|
|
25524
|
+
onResult?.(result);
|
|
25525
|
+
}
|
|
25526
|
+
return results;
|
|
25527
|
+
}
|
|
25528
|
+
function emitReport(report) {
|
|
25529
|
+
console.log(JSON.stringify(report, null, 2));
|
|
25530
|
+
}
|
|
25531
|
+
function renderResult({ name, ok, fix }) {
|
|
25532
|
+
if (ok) {
|
|
25533
|
+
console.log(` \u2714 ${name}`);
|
|
25534
|
+
return;
|
|
25535
|
+
}
|
|
25536
|
+
console.log(` \u2716 ${name}`);
|
|
25537
|
+
if (fix) console.log(` \u2192 ${fix}`);
|
|
25538
|
+
}
|
|
25539
|
+
async function doctor(options) {
|
|
25540
|
+
const jsonMode = options.json === true;
|
|
25541
|
+
if (jsonMode) setStderrOnly(true);
|
|
25542
|
+
log.info("Running diagnostics...\n");
|
|
25543
|
+
const ctx = await resolveDoctorContext();
|
|
25544
|
+
if (!ctx) {
|
|
25545
|
+
const notInitialized = {
|
|
25546
|
+
name: "TeamAI is not initialized",
|
|
25547
|
+
ok: false,
|
|
25548
|
+
fix: "Run `teamai init <repo-url>` in a project, or add `--scope user` for all projects"
|
|
25549
|
+
};
|
|
25550
|
+
if (jsonMode) {
|
|
25551
|
+
emitReport({ ok: false, scope: null, checks: [notInitialized] });
|
|
25286
25552
|
} else {
|
|
25287
|
-
console.log(
|
|
25288
|
-
|
|
25289
|
-
|
|
25553
|
+
console.log(" Scope: not initialized\n");
|
|
25554
|
+
renderResult(notInitialized);
|
|
25555
|
+
console.log("");
|
|
25290
25556
|
}
|
|
25557
|
+
log.warn("Initialization is required before diagnostics can run.");
|
|
25558
|
+
return false;
|
|
25291
25559
|
}
|
|
25560
|
+
const { localConfig, toolPaths, baseDir } = ctx;
|
|
25561
|
+
const scope = localConfig.scope ?? "user";
|
|
25562
|
+
if (!jsonMode) {
|
|
25563
|
+
const scopeLabel = `${scope}${scope === "project" && localConfig.projectRoot ? ` (${localConfig.projectRoot})` : ""}`;
|
|
25564
|
+
console.log(` Scope: ${scopeLabel}
|
|
25565
|
+
`);
|
|
25566
|
+
}
|
|
25567
|
+
const results = await runChecks(await buildChecks(ctx), jsonMode ? void 0 : renderResult);
|
|
25568
|
+
let allPassed = results.every((r) => r.ok);
|
|
25292
25569
|
const { pkgDoctorReport: pkgDoctorReport2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
25293
25570
|
const packageReport = await pkgDoctorReport2(localConfig, process.cwd());
|
|
25571
|
+
if (packageReport && !packageReport.allPassed) allPassed = false;
|
|
25572
|
+
const codexNote = await hasInstalledCodexHooks(toolPaths, baseDir) ? codexTrustReminder() : null;
|
|
25573
|
+
if (jsonMode) {
|
|
25574
|
+
emitReport({
|
|
25575
|
+
ok: allPassed,
|
|
25576
|
+
scope,
|
|
25577
|
+
checks: results,
|
|
25578
|
+
// pkgDoctorReport renders its own lines; they are human text, not checks.
|
|
25579
|
+
...packageReport ? { packages: { ok: packageReport.allPassed, lines: packageReport.lines } } : {},
|
|
25580
|
+
...codexNote ? { notes: [codexNote] } : {}
|
|
25581
|
+
});
|
|
25582
|
+
return allPassed;
|
|
25583
|
+
}
|
|
25294
25584
|
if (packageReport) {
|
|
25295
25585
|
for (const line of packageReport.lines) console.log(line);
|
|
25296
|
-
if (!packageReport.allPassed) allPassed = false;
|
|
25297
25586
|
}
|
|
25298
|
-
if (
|
|
25587
|
+
if (codexNote) {
|
|
25299
25588
|
console.log("");
|
|
25300
|
-
log.info(
|
|
25589
|
+
log.info(codexNote);
|
|
25301
25590
|
}
|
|
25302
25591
|
console.log("");
|
|
25303
25592
|
if (allPassed) {
|
|
@@ -25314,6 +25603,7 @@ var init_doctor = __esm({
|
|
|
25314
25603
|
init_fs();
|
|
25315
25604
|
init_logger();
|
|
25316
25605
|
init_types();
|
|
25606
|
+
init_base();
|
|
25317
25607
|
init_hooks2();
|
|
25318
25608
|
init_home();
|
|
25319
25609
|
}
|
|
@@ -25897,6 +26187,12 @@ async function saveTagsScopeConfig(localConfig) {
|
|
|
25897
26187
|
} else {
|
|
25898
26188
|
await saveLocalConfig(localConfig);
|
|
25899
26189
|
}
|
|
26190
|
+
try {
|
|
26191
|
+
const state = await loadStateForScope(localConfig);
|
|
26192
|
+
state.lastPullRev = null;
|
|
26193
|
+
await saveStateForScope(state, localConfig);
|
|
26194
|
+
} catch {
|
|
26195
|
+
}
|
|
25900
26196
|
}
|
|
25901
26197
|
async function tagsList(options) {
|
|
25902
26198
|
const localConfig = await resolveTagsScope();
|
|
@@ -26135,7 +26431,7 @@ function opencodePluginTargets(baseDir, scope) {
|
|
|
26135
26431
|
}
|
|
26136
26432
|
return targets;
|
|
26137
26433
|
}
|
|
26138
|
-
async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, teamRuleNames, teamAgentNames, hookTargets, scope) {
|
|
26434
|
+
async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, teamRuleNames, teamAgentNames, hookTargets, standaloneHookManifestPath, scope) {
|
|
26139
26435
|
const res = {
|
|
26140
26436
|
hookFiles: [],
|
|
26141
26437
|
openclawHookDirs: [],
|
|
@@ -26145,7 +26441,16 @@ async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, te
|
|
|
26145
26441
|
ruleFiles: [],
|
|
26146
26442
|
agentFiles: []
|
|
26147
26443
|
};
|
|
26148
|
-
if (
|
|
26444
|
+
if (toolPath.hooks) {
|
|
26445
|
+
const hooksPath = path74.join(baseDir, toolPath.hooks);
|
|
26446
|
+
if (await pathExists(hooksPath) && (await hasTeamaiHooks(hooksPath, tool, standaloneHookManifestPath) || isEmptyHooksResidue(await readJson(hooksPath)))) {
|
|
26447
|
+
res.hookFiles.push({
|
|
26448
|
+
path: hooksPath,
|
|
26449
|
+
tool,
|
|
26450
|
+
manifestPath: standaloneHookManifestPath
|
|
26451
|
+
});
|
|
26452
|
+
}
|
|
26453
|
+
} else if (tool === "opencode") {
|
|
26149
26454
|
const { resolveOpencodePluginDir: resolveOpencodePluginDir2, OPENCODE_HOOK_FILE: OPENCODE_HOOK_FILE2 } = await Promise.resolve().then(() => (init_opencode_hooks(), opencode_hooks_exports));
|
|
26150
26455
|
for (const target of opencodePluginTargets(baseDir, scope)) {
|
|
26151
26456
|
const pluginDir = resolveOpencodePluginDir2(target.baseDir, target.scope);
|
|
@@ -26232,6 +26537,10 @@ async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, te
|
|
|
26232
26537
|
async function buildRemovalPlan(localConfig, teamConfig, agentFilter) {
|
|
26233
26538
|
const baseDir = resolveBaseDir(localConfig);
|
|
26234
26539
|
const teamaiHome = getDataHome(localConfig);
|
|
26540
|
+
const standaloneHookManifestPath = getManagedHooksPath(
|
|
26541
|
+
localConfig.scope,
|
|
26542
|
+
localConfig.projectRoot
|
|
26543
|
+
);
|
|
26235
26544
|
const repoPath = localConfig.repo.localPath;
|
|
26236
26545
|
const teamSkillNames = await collectTeamSkillNames(repoPath);
|
|
26237
26546
|
for (const name of BUILTIN_SKILL_NAMES) teamSkillNames.add(name);
|
|
@@ -26268,11 +26577,12 @@ async function buildRemovalPlan(localConfig, teamConfig, agentFilter) {
|
|
|
26268
26577
|
await discoverToolResources(
|
|
26269
26578
|
tool,
|
|
26270
26579
|
toolPath,
|
|
26271
|
-
|
|
26580
|
+
resolveToolBaseDir(tool, localConfig),
|
|
26272
26581
|
teamSkillNames,
|
|
26273
26582
|
teamRuleNames,
|
|
26274
26583
|
teamAgentNames,
|
|
26275
26584
|
hookTargets,
|
|
26585
|
+
standaloneHookManifestPath,
|
|
26276
26586
|
localConfig.scope
|
|
26277
26587
|
)
|
|
26278
26588
|
);
|
|
@@ -26890,16 +27200,16 @@ async function hooksList(_options) {
|
|
|
26890
27200
|
const { localConfig, teamConfig } = await autoDetectInit();
|
|
26891
27201
|
const { baseDir } = resolveHookScope(localConfig);
|
|
26892
27202
|
const rows = [];
|
|
26893
|
-
for (const [tool, paths] of Object.entries(teamConfig
|
|
26894
|
-
|
|
27203
|
+
for (const [tool, paths] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
27204
|
+
const hookPath = paths.hooks ? path76.join(resolveToolBaseDir(tool, localConfig), paths.hooks) : paths.settings ? path76.join(baseDir, paths.settings) : void 0;
|
|
27205
|
+
if (!hookPath) {
|
|
26895
27206
|
rows.push({ tool, status: "not configured", settingsPath: "no settings configured" });
|
|
26896
27207
|
continue;
|
|
26897
27208
|
}
|
|
26898
|
-
const settingsPath = path76.join(baseDir, paths.settings);
|
|
26899
27209
|
rows.push({
|
|
26900
27210
|
tool,
|
|
26901
|
-
status: await getHookStatus(
|
|
26902
|
-
settingsPath: formatDisplayPath(
|
|
27211
|
+
status: await getHookStatus(hookPath, tool),
|
|
27212
|
+
settingsPath: formatDisplayPath(hookPath)
|
|
26903
27213
|
});
|
|
26904
27214
|
}
|
|
26905
27215
|
console.log(formatHooksList(rows));
|
|
@@ -26928,6 +27238,18 @@ async function hooksRemove(_options) {
|
|
|
26928
27238
|
const { localConfig, teamConfig } = await autoDetectInit();
|
|
26929
27239
|
const { baseDir, manifestPath } = resolveHookScope(localConfig);
|
|
26930
27240
|
await reconcileHooksToAllTools(teamConfig.toolPaths, baseDir, [], manifestPath, { removeAll: true });
|
|
27241
|
+
const copilotPaths = scopedToolPaths(teamConfig, localConfig)[COPILOT_TOOL_ID];
|
|
27242
|
+
if (copilotPaths?.hooks) {
|
|
27243
|
+
await reconcileHooks(
|
|
27244
|
+
path76.join(resolveToolBaseDir(COPILOT_TOOL_ID, localConfig), copilotPaths.hooks),
|
|
27245
|
+
COPILOT_TOOL_ID,
|
|
27246
|
+
[],
|
|
27247
|
+
{
|
|
27248
|
+
manifestPath: getManagedHooksPath(localConfig.scope, localConfig.projectRoot),
|
|
27249
|
+
removeAll: true
|
|
27250
|
+
}
|
|
27251
|
+
);
|
|
27252
|
+
}
|
|
26931
27253
|
await sweepLegacyProjectHooks(teamConfig.toolPaths, localConfig);
|
|
26932
27254
|
log.success("Hooks removed from all AI tool settings");
|
|
26933
27255
|
}
|
|
@@ -43317,16 +43639,16 @@ membersCmd.command("list").description("List team members").action(async () => {
|
|
|
43317
43639
|
const { listMembers: listMembers2 } = await Promise.resolve().then(() => (init_members(), members_exports));
|
|
43318
43640
|
await listMembers2(globalOpts);
|
|
43319
43641
|
});
|
|
43320
|
-
program.command("remove <type> <names...>").description("Remove resource(s) from team repo and all local AI tools (type: skills|rules|agents|mcp)").action(async (type, names) => {
|
|
43642
|
+
program.command("remove <type> <names...>").description("Remove resource(s) from team repo and all local AI tools (type: skills|rules|agents|mcp)").option("--force", "Skip confirmation prompt").action(async (type, names, cmdOpts) => {
|
|
43321
43643
|
const globalOpts = program.opts();
|
|
43322
43644
|
const { remove: remove3 } = await Promise.resolve().then(() => (init_remove(), remove_exports));
|
|
43323
|
-
await remove3(type, names, globalOpts);
|
|
43645
|
+
await remove3(type, names, { ...globalOpts, ...cmdOpts });
|
|
43324
43646
|
});
|
|
43325
43647
|
registerPackagesCommand(program);
|
|
43326
|
-
program.command("doctor").description("Diagnose configuration issues").action(async () => {
|
|
43648
|
+
program.command("doctor").description("Diagnose configuration issues").option("--json", "Output the report as JSON (suitable for CI)").action(async (cmdOpts) => {
|
|
43327
43649
|
const globalOpts = program.opts();
|
|
43328
43650
|
const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
43329
|
-
const allPassed = await doctor2(globalOpts);
|
|
43651
|
+
const allPassed = await doctor2({ ...globalOpts, ...cmdOpts });
|
|
43330
43652
|
if (!allPassed) process.exitCode = 1;
|
|
43331
43653
|
});
|
|
43332
43654
|
var rolesCmd = program.command("roles").description("Manage team roles and resource namespaces").action(async () => {
|