@team-ai/teamai-cli 0.0.0-stage → 0.22.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 +1416 -0
- package/LICENSE +13 -0
- package/README.ja.md +177 -0
- package/README.ko.md +177 -0
- package/README.md +176 -2
- package/README.th.md +177 -0
- package/README.zh-CN.md +183 -0
- package/agents/teamai-recall.md +406 -0
- package/dist/cli.js +136359 -0
- package/dist/index.js +9 -0
- package/package.json +100 -4
- package/skill-data/core/SKILL.md +250 -0
- package/skill-data/core/references/commands.md +471 -0
- package/skill-data/core/references/contribute-member.md +161 -0
- package/skill-data/core/references/troubleshooting.md +419 -0
- package/skill-data/setup/SKILL.md +80 -0
- package/skill-data/setup/references/join-member.md +189 -0
- package/skill-data/setup/references/manage-admin.md +354 -0
- package/skill-data/setup/references/provider-tgit.md +101 -0
- package/skill-data/setup/references/setup-admin.md +312 -0
- package/skill-data/setup/references/uninstall.md +180 -0
- package/skill-data/share/SKILL.md +70 -0
- package/skill-data/share/references/doc-template.md +44 -0
- package/skill-data/wiki/SKILL.md +317 -0
- package/skill-data/wiki/references/agents/graph-rag-agent.md +344 -0
- package/skill-data/wiki/references/agents/kb-doc-generator.md +325 -0
- package/skill-data/wiki/references/methodology/phase0-collection.md +54 -0
- package/skill-data/wiki/references/methodology/phase1-reverse-engineering.md +89 -0
- package/skill-data/wiki/references/methodology/phase2-document-types.md +341 -0
- package/skill-data/wiki/references/methodology/phase3-ai-enhancement.md +164 -0
- package/skill-data/wiki/references/methodology/phase4-quality.md +232 -0
- package/skill-data/wiki/references/overview.md +125 -0
- package/skill-data/wiki/references/phases/k1-reverse-engineering.md +118 -0
- package/skill-data/wiki/references/phases/k2-documents.md +68 -0
- package/skill-data/wiki/references/phases/k3-ai-native.md +121 -0
- package/skill-data/wiki/references/phases/k4-quality.md +190 -0
- package/skill-data/wiki/references/phases/phase0-init.md +113 -0
- package/skill-data/wiki/references/templates/project-overview.md +149 -0
- package/skill-data/wiki/scripts/scan_repo.py +224 -0
- package/skill-data/wiki/scripts/validate_kb.py +256 -0
- package/skills/teamai/SKILL.md +46 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
validate_kb.py: knowledge base quality validation tool
|
|
4
|
+
|
|
5
|
+
Purpose: in the Phase 4 quality assessment stage, automatically check the generated knowledge base for:
|
|
6
|
+
1. Link integrity (dead link detection)
|
|
7
|
+
2. search-anchor coverage
|
|
8
|
+
3. AI Quick Reference table coverage
|
|
9
|
+
4. Bidirectional link integrity
|
|
10
|
+
5. README index coverage
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
python3 validate_kb.py /path/to/knowledge-base-dir
|
|
14
|
+
python3 validate_kb.py /path/to/knowledge-base-dir --verbose
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
import argparse
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from collections import defaultdict
|
|
23
|
+
|
|
24
|
+
# Markdown link regex: [text](path) or [text](path#anchor)
|
|
25
|
+
LINK_PATTERN = re.compile(r'\[([^\]]*)\]\(([^)]+)\)')
|
|
26
|
+
# search-anchor regex
|
|
27
|
+
ANCHOR_PATTERN = re.compile(r'<!--\s*search-anchor\s*:(.*?)-->', re.DOTALL)
|
|
28
|
+
# AI Quick Reference table regex. Matches both the current English heading and the
|
|
29
|
+
# legacy Chinese heading so knowledge bases built with earlier releases still validate.
|
|
30
|
+
# Legacy knowledge bases carry the Chinese heading; matched by code point so the source stays ASCII-only.
|
|
31
|
+
AI_TABLE_PATTERN = re.compile(r'##\s*🤖\s*AI\s*(?:Quick\s*Reference|\u5feb\u901f\u7406\u89e3)', re.IGNORECASE)
|
|
32
|
+
# Bidirectional link: a link back to the main / technical architecture document.
|
|
33
|
+
# Bilingual for the same reason as AI_TABLE_PATTERN.
|
|
34
|
+
BACK_LINK_PATTERN = re.compile(
|
|
35
|
+
r'\[📘.*(?:Technical\s*Architecture|\u4e3b\u67b6\u6784|\u6280\u672f\u67b6\u6784)|Position in the overall architecture|\u5728\u6574\u4f53\u67b6\u6784\u4e2d\u7684\u4f4d\u7f6e',
|
|
36
|
+
re.IGNORECASE,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_md_files(kb_dir: Path) -> list:
|
|
41
|
+
"""Find all .md files"""
|
|
42
|
+
md_files = []
|
|
43
|
+
for root, dirs, files in os.walk(kb_dir):
|
|
44
|
+
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
45
|
+
for f in files:
|
|
46
|
+
if f.endswith('.md'):
|
|
47
|
+
md_files.append(Path(root) / f)
|
|
48
|
+
return sorted(md_files)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_links(md_file: Path, kb_dir: Path) -> list:
|
|
52
|
+
"""Check that the links in the file resolve"""
|
|
53
|
+
broken = []
|
|
54
|
+
try:
|
|
55
|
+
content = md_file.read_text(encoding='utf-8', errors='ignore')
|
|
56
|
+
except OSError:
|
|
57
|
+
return [("READ_ERROR", str(md_file), "cannot read file")]
|
|
58
|
+
|
|
59
|
+
for match in LINK_PATTERN.finditer(content):
|
|
60
|
+
link_text = match.group(1)
|
|
61
|
+
link_target = match.group(2)
|
|
62
|
+
|
|
63
|
+
# Skip external links and anchor-only links
|
|
64
|
+
if link_target.startswith(('http://', 'https://', 'mailto:', '#')):
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Split path and anchor
|
|
68
|
+
path_part = link_target.split('#')[0]
|
|
69
|
+
if not path_part:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
# Resolve the relative path
|
|
73
|
+
target_path = (md_file.parent / path_part).resolve()
|
|
74
|
+
if not target_path.exists():
|
|
75
|
+
rel = str(md_file.relative_to(kb_dir))
|
|
76
|
+
broken.append((rel, link_target, link_text))
|
|
77
|
+
|
|
78
|
+
return broken
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def check_anchor(md_file: Path) -> bool:
|
|
82
|
+
"""Check whether the file contains a search-anchor"""
|
|
83
|
+
try:
|
|
84
|
+
content = md_file.read_text(encoding='utf-8', errors='ignore')
|
|
85
|
+
return bool(ANCHOR_PATTERN.search(content))
|
|
86
|
+
except OSError:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def check_ai_table(md_file: Path) -> bool:
|
|
91
|
+
"""Check whether the file contains the AI Quick Reference table"""
|
|
92
|
+
try:
|
|
93
|
+
content = md_file.read_text(encoding='utf-8', errors='ignore')
|
|
94
|
+
return bool(AI_TABLE_PATTERN.search(content))
|
|
95
|
+
except OSError:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def check_back_link(md_file: Path) -> bool:
|
|
100
|
+
"""Check whether the component document links back to the main architecture document"""
|
|
101
|
+
try:
|
|
102
|
+
content = md_file.read_text(encoding='utf-8', errors='ignore')
|
|
103
|
+
return bool(BACK_LINK_PATTERN.search(content))
|
|
104
|
+
except OSError:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def check_readme_coverage(kb_dir: Path, md_files: list) -> tuple:
|
|
109
|
+
"""Check whether the README indexes every .md file"""
|
|
110
|
+
readme_path = kb_dir / "README.md"
|
|
111
|
+
if not readme_path.exists():
|
|
112
|
+
return [], md_files
|
|
113
|
+
|
|
114
|
+
readme_content = readme_path.read_text(encoding='utf-8', errors='ignore')
|
|
115
|
+
covered = []
|
|
116
|
+
uncovered = []
|
|
117
|
+
|
|
118
|
+
for f in md_files:
|
|
119
|
+
if f.name == "README.md":
|
|
120
|
+
continue
|
|
121
|
+
# Check whether the README mentions this file
|
|
122
|
+
fname_no_ext = f.stem
|
|
123
|
+
if fname_no_ext in readme_content or f.name in readme_content:
|
|
124
|
+
covered.append(f)
|
|
125
|
+
else:
|
|
126
|
+
uncovered.append(f)
|
|
127
|
+
|
|
128
|
+
return covered, uncovered
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main():
|
|
132
|
+
parser = argparse.ArgumentParser(description="Knowledge base quality validation tool")
|
|
133
|
+
parser.add_argument("kb_dir", help="Path of the knowledge base directory")
|
|
134
|
+
parser.add_argument("--verbose", "-v", action="store_true", help="Print details")
|
|
135
|
+
args = parser.parse_args()
|
|
136
|
+
|
|
137
|
+
kb_dir = Path(args.kb_dir).resolve()
|
|
138
|
+
if not kb_dir.is_dir():
|
|
139
|
+
print(f"Error: {kb_dir} is not a valid directory", file=sys.stderr)
|
|
140
|
+
sys.exit(1)
|
|
141
|
+
|
|
142
|
+
md_files = find_md_files(kb_dir)
|
|
143
|
+
if not md_files:
|
|
144
|
+
print(f"Warning: no .md files found in {kb_dir}")
|
|
145
|
+
sys.exit(0)
|
|
146
|
+
|
|
147
|
+
# Filter the component design documents (files starting with a number)
|
|
148
|
+
component_docs = [f for f in md_files if re.match(r'^\d+_', f.name)]
|
|
149
|
+
|
|
150
|
+
print("=" * 70)
|
|
151
|
+
print(f" Knowledge base quality validation report")
|
|
152
|
+
print(f" Directory: {kb_dir}")
|
|
153
|
+
print(f" Files: {len(md_files)} .md files ({len(component_docs)} component documents)")
|
|
154
|
+
print("=" * 70)
|
|
155
|
+
|
|
156
|
+
total_score = 0
|
|
157
|
+
max_score = 0
|
|
158
|
+
|
|
159
|
+
# 1. Link integrity
|
|
160
|
+
print(f"\n## 1. Link integrity check\n")
|
|
161
|
+
all_broken = []
|
|
162
|
+
for f in md_files:
|
|
163
|
+
broken = check_links(f, kb_dir)
|
|
164
|
+
all_broken.extend(broken)
|
|
165
|
+
|
|
166
|
+
if all_broken:
|
|
167
|
+
print(f"❌ Found {len(all_broken)} dead links:")
|
|
168
|
+
for src, target, text in all_broken[:20]:
|
|
169
|
+
print(f" {src} → [{text}]({target})")
|
|
170
|
+
if len(all_broken) > 20:
|
|
171
|
+
print(f" ... and {len(all_broken) - 20} more")
|
|
172
|
+
else:
|
|
173
|
+
print(f"✅ All links valid ({len(md_files)} files checked)")
|
|
174
|
+
total_score += 20
|
|
175
|
+
max_score += 20
|
|
176
|
+
|
|
177
|
+
# 2. search-anchor coverage
|
|
178
|
+
print(f"\n## 2. Search-Anchor coverage\n")
|
|
179
|
+
has_anchor = sum(1 for f in md_files if check_anchor(f))
|
|
180
|
+
anchor_pct = has_anchor / len(md_files) * 100 if md_files else 0
|
|
181
|
+
print(f"{'✅' if anchor_pct >= 80 else '⚠️'} {has_anchor}/{len(md_files)} files have a search-anchor ({anchor_pct:.0f}%)")
|
|
182
|
+
if args.verbose:
|
|
183
|
+
for f in md_files:
|
|
184
|
+
if not check_anchor(f):
|
|
185
|
+
print(f" Missing: {f.relative_to(kb_dir)}")
|
|
186
|
+
if anchor_pct >= 80:
|
|
187
|
+
total_score += 20
|
|
188
|
+
elif anchor_pct >= 50:
|
|
189
|
+
total_score += 10
|
|
190
|
+
max_score += 20
|
|
191
|
+
|
|
192
|
+
# 3. AI Quick Reference table coverage (component documents only)
|
|
193
|
+
print(f"\n## 3. AI Quick Reference table coverage (component documents)\n")
|
|
194
|
+
if component_docs:
|
|
195
|
+
has_ai_table = sum(1 for f in component_docs if check_ai_table(f))
|
|
196
|
+
ai_pct = has_ai_table / len(component_docs) * 100
|
|
197
|
+
print(f"{'✅' if ai_pct >= 90 else '⚠️'} {has_ai_table}/{len(component_docs)} component documents have an AI Quick Reference table ({ai_pct:.0f}%)")
|
|
198
|
+
if args.verbose:
|
|
199
|
+
for f in component_docs:
|
|
200
|
+
if not check_ai_table(f):
|
|
201
|
+
print(f" Missing: {f.relative_to(kb_dir)}")
|
|
202
|
+
if ai_pct >= 90:
|
|
203
|
+
total_score += 20
|
|
204
|
+
elif ai_pct >= 60:
|
|
205
|
+
total_score += 10
|
|
206
|
+
else:
|
|
207
|
+
print("⚠️ No numbered component documents found")
|
|
208
|
+
max_score += 20
|
|
209
|
+
|
|
210
|
+
# 4. Bidirectional link check (component documents link back to the main architecture)
|
|
211
|
+
print(f"\n## 4. Bidirectional link check (component → main architecture)\n")
|
|
212
|
+
if component_docs:
|
|
213
|
+
has_back = sum(1 for f in component_docs if check_back_link(f))
|
|
214
|
+
back_pct = has_back / len(component_docs) * 100
|
|
215
|
+
print(f"{'✅' if back_pct >= 90 else '⚠️'} {has_back}/{len(component_docs)} component documents link back to the main architecture ({back_pct:.0f}%)")
|
|
216
|
+
if back_pct >= 90:
|
|
217
|
+
total_score += 20
|
|
218
|
+
elif back_pct >= 60:
|
|
219
|
+
total_score += 10
|
|
220
|
+
else:
|
|
221
|
+
print("⚠️ No numbered component documents found")
|
|
222
|
+
max_score += 20
|
|
223
|
+
|
|
224
|
+
# 5. README index coverage
|
|
225
|
+
print(f"\n## 5. README index coverage\n")
|
|
226
|
+
covered, uncovered = check_readme_coverage(kb_dir, md_files)
|
|
227
|
+
if (kb_dir / "README.md").exists():
|
|
228
|
+
cover_pct = len(covered) / (len(covered) + len(uncovered)) * 100 if (covered or uncovered) else 100
|
|
229
|
+
print(f"{'✅' if cover_pct >= 90 else '⚠️'} README indexes {len(covered)}/{len(covered)+len(uncovered)} documents ({cover_pct:.0f}%)")
|
|
230
|
+
if uncovered and args.verbose:
|
|
231
|
+
print(" Not indexed:")
|
|
232
|
+
for f in uncovered[:10]:
|
|
233
|
+
print(f" {f.relative_to(kb_dir)}")
|
|
234
|
+
if cover_pct >= 90:
|
|
235
|
+
total_score += 20
|
|
236
|
+
elif cover_pct >= 60:
|
|
237
|
+
total_score += 10
|
|
238
|
+
else:
|
|
239
|
+
print("❌ README.md not found")
|
|
240
|
+
max_score += 20
|
|
241
|
+
|
|
242
|
+
# Summary
|
|
243
|
+
final_pct = total_score / max_score * 100 if max_score else 0
|
|
244
|
+
print(f"\n{'=' * 70}")
|
|
245
|
+
print(f" Overall score: {total_score}/{max_score} ({final_pct:.0f}%)")
|
|
246
|
+
if final_pct >= 90:
|
|
247
|
+
print(f" Rating: ✅ Excellent. The knowledge base meets the quality bar")
|
|
248
|
+
elif final_pct >= 70:
|
|
249
|
+
print(f" Rating: ⚠️ Good. Fixing the issues above is recommended")
|
|
250
|
+
else:
|
|
251
|
+
print(f" Rating: ❌ Needs improvement. There are many quality issues")
|
|
252
|
+
print(f"{'=' * 70}")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
if __name__ == "__main__":
|
|
256
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: teamai
|
|
3
|
+
description: >-
|
|
4
|
+
Make every team AI native — TeamAI syncs a team's AI skills, rules, docs and env across AI coding
|
|
5
|
+
tools. Use when the task operates on team-shared AI configuration or team knowledge: setting up a
|
|
6
|
+
team repo, joining one, managing members, syncing with pull or push, or checking team status.
|
|
7
|
+
Also use to build or query a codebase knowledge base for a large multi-repo project (architecture
|
|
8
|
+
analysis, architecture reverse-engineering, code-to-knowledge, team-wiki-codebase, architecture wiki),
|
|
9
|
+
and to share what a session taught you back to the team (share session learnings, contribute a
|
|
10
|
+
learning, share what I learned with my team), including
|
|
11
|
+
after a friction reminder. Triggers include "set up teamai", "join the team repo", "sync team
|
|
12
|
+
skills", "team wiki", "share what I learned", and running /teamai. Talking about a team needs no
|
|
13
|
+
skill; operating on what the team shares does.
|
|
14
|
+
allowed-tools: Bash(teamai skill:*), Bash(npx teamai-cli skill:*)
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# teamai
|
|
18
|
+
|
|
19
|
+
Make every team AI native — one shared foundation for the skills, rules, docs and env a team works with.
|
|
20
|
+
|
|
21
|
+
Install: `npm i -g teamai-cli@latest` (Node.js >= 20). If `teamai skill get` is not recognised, the installed CLI predates it; upgrade the same way.
|
|
22
|
+
|
|
23
|
+
## Start here
|
|
24
|
+
|
|
25
|
+
This file is a discovery stub, not the usage guide. Load the workflow from the CLI before running anything, so the instructions match the installed version:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
teamai skill get core # daily work: routing, pull, push, status, doctor
|
|
29
|
+
teamai skill get core --full # adds the full command reference and troubleshooting
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The CLI serves skill content that always matches the installed version, so instructions never go stale. The content in this stub cannot change between releases, which is why it just points at `skill get`.
|
|
33
|
+
|
|
34
|
+
## Specialized workflows
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
teamai skill get setup # day 0: create a team repo (admin) or join one (member), manage, uninstall
|
|
38
|
+
teamai skill get wiki # large multi-repo codebase: architecture reverse-engineering and knowledge base
|
|
39
|
+
teamai skill get share # turn what this session taught you into a team learning (needs recall on)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Publishing a skill, rule or doc the user already has is in `core`; it needs no recall.
|
|
43
|
+
|
|
44
|
+
A friction reminder at the end of a turn means `teamai skill get share`.
|
|
45
|
+
|
|
46
|
+
`teamai skill list` shows everything the installed version serves. `teamai skill path <name>` prints the directory holding a skill's scripts and templates.
|