knowme-careerforge-skill 0.0.7 → 0.0.8
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/AGENT.md +7 -6
- package/ARCHITECTURE.md +18 -15
- package/README.md +23 -15
- package/README.zh-CN.md +23 -15
- package/SKILL.md +26 -33
- package/agents/codex/knowme-careerforge.yaml +1 -1
- package/agents/opencode/skill.yaml +1 -1
- package/dist/cli/src/index.js +22 -14
- package/package.json +1 -1
- package/references/01-evidence-mining.md +80 -3
- package/references/02-career-goal.md +35 -15
- package/references/03-jd-analysis.md +10 -0
- package/references/04-template-selection.md +7 -2
- package/references/05-html-canvas-tokens.md +4 -2
- package/references/06-qa-and-rendering.md +39 -5
- package/references/07-artifact-contract.md +21 -7
- package/scripts/Agent.md +3 -3
- package/scripts/build/build-gallery.py +2 -1
- package/scripts/build/release.py +3 -2
- package/scripts/evidence/analyze-jd.py +49 -8
- package/scripts/evidence/extract-evidence.py +17 -391
- package/scripts/pipeline/forge.py +159 -85
- package/scripts/rendering/browser-engine.js +5 -1
- package/scripts/template/instantiate-resume.py +5 -3
- package/skill.json +1 -1
- package/src/knowledge/career/seniority-signals.json +1 -0
- package/src/knowledge/index.json +176 -2
- package/src/knowledge/resume/action-verbs.json +3 -2
- package/src/knowledge/resume/fab-model.json +5 -4
- package/src/knowledge/resume-schema.json +1 -1
- package/src/knowledge/templates.json +174 -0
- package/src/references/01-evidence-mining.md +80 -3
- package/src/references/02-career-goal.md +35 -15
- package/src/references/03-jd-analysis.md +10 -0
- package/src/references/04-template-selection.md +7 -2
- package/src/references/05-html-canvas-tokens.md +4 -2
- package/src/references/06-qa-and-rendering.md +39 -5
- package/src/references/07-artifact-contract.md +21 -7
- package/src/templates/academic-research/canvas.html +2 -1
- package/src/templates/classic/canvas.html +2 -1
- package/src/templates/classic/style.css +90 -28
- package/src/templates/common/base.css +19 -3
- package/src/templates/common/canvas-bindings.css +247 -12
- package/src/templates/compact-dense/style.css +5 -1
- package/src/templates/creative-tech/style.css +30 -1
- package/src/templates/data-analyst/style.css +31 -1
- package/src/templates/editorial/README.md +7 -0
- package/src/templates/editorial/canvas.html +22 -0
- package/src/templates/editorial/metadata.json +36 -0
- package/src/templates/editorial/sample-profile.json +86 -0
- package/src/templates/editorial/style.css +24 -0
- package/src/templates/executive/style.css +52 -18
- package/src/templates/graduate/README.md +7 -0
- package/src/templates/graduate/canvas.html +22 -0
- package/src/templates/graduate/metadata.json +38 -0
- package/src/templates/graduate/sample-profile.json +60 -0
- package/src/templates/graduate/style.css +24 -0
- package/src/templates/international-flow/canvas.html +2 -1
- package/src/templates/minimal/canvas.html +3 -2
- package/src/templates/minimal/style.css +6 -0
- package/src/templates/modern/style.css +43 -55
- package/src/templates/project-focus/README.md +7 -0
- package/src/templates/project-focus/canvas.html +22 -0
- package/src/templates/project-focus/metadata.json +36 -0
- package/src/templates/project-focus/sample-profile.json +93 -0
- package/src/templates/project-focus/style.css +24 -0
- package/src/templates/startup-generalist/canvas.html +2 -1
- package/src/templates/startup-generalist/style.css +15 -8
|
@@ -1,395 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"""
|
|
2
|
+
"""Retired compatibility entry: repository contents are not candidate evidence."""
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
6
5
|
|
|
7
|
-
import sys, os, json, argparse, re, subprocess
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Dict, List, Any, Optional
|
|
10
|
-
|
|
11
|
-
def get_project_root() -> Path:
|
|
12
|
-
curr = Path(__file__).resolve().parent
|
|
13
|
-
for _ in range(5):
|
|
14
|
-
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
15
|
-
return curr
|
|
16
|
-
curr = curr.parent
|
|
17
|
-
return Path.cwd()
|
|
18
|
-
|
|
19
|
-
TECH_SIGNATURES = {
|
|
20
|
-
# AI / LLM & Agents
|
|
21
|
-
"LangChain": ["langchain", "langchain-core", "@langchain/core"],
|
|
22
|
-
"LangGraph": ["langgraph", "@langchain/langgraph"],
|
|
23
|
-
"LlamaIndex": ["llama-index", "llamaindex"],
|
|
24
|
-
"Qdrant": ["qdrant-client", "@qdrant/js-client-rest"],
|
|
25
|
-
"Milvus": ["pymilvus", "@zilliz/milvus2-sdk-node"],
|
|
26
|
-
"FastAPI": ["fastapi", "uvicorn"],
|
|
27
|
-
"PyTorch": ["torch", "torchvision"],
|
|
28
|
-
"OpenAI SDK": ["openai", "@openai/openai"],
|
|
29
|
-
# Web & Fullstack
|
|
30
|
-
"TypeScript": ["typescript", "ts-node", "@types/node"],
|
|
31
|
-
"React": ["react", "react-dom", "next"],
|
|
32
|
-
"Next.js": ["next"],
|
|
33
|
-
"Vue.js": ["vue", "nuxt"],
|
|
34
|
-
"Node.js": ["express", "koa", "nestjs", "@nestjs/core", "fastify"],
|
|
35
|
-
"Tailwind CSS": ["tailwindcss", "@tailwindcss/postcss"],
|
|
36
|
-
"Playwright": ["playwright", "@playwright/test"],
|
|
37
|
-
"GraphQL": ["graphql", "apollo-server", "@apollo/client"],
|
|
38
|
-
# Backend & Systems
|
|
39
|
-
"Go / Golang": ["github.com/gin-gonic/gin", "google.golang.org/grpc"],
|
|
40
|
-
"Rust": ["tokio", "serde", "actix-web", "axum"],
|
|
41
|
-
"Spring Boot": ["spring-boot", "org.springframework.boot"],
|
|
42
|
-
# Database & Cache
|
|
43
|
-
"PostgreSQL": ["pg", "psycopg2", "asyncpg", "typeorm", "prisma"],
|
|
44
|
-
"MySQL": ["mysql", "mysql2", "pymysql"],
|
|
45
|
-
"Redis": ["redis", "ioredis", "aioredis"],
|
|
46
|
-
"Elasticsearch": ["elasticsearch", "@elastic/elasticsearch"],
|
|
47
|
-
# DevOps & Infrastructure
|
|
48
|
-
"Docker": ["Dockerfile", "docker-compose.yml", "docker-compose.yaml"],
|
|
49
|
-
"Kubernetes": ["k8s", "helm", "Deployment", "StatefulSet"],
|
|
50
|
-
"GitHub Actions": [".github/workflows"]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
def run_cmd(cmd: List[str], cwd: Optional[Path] = None) -> str:
|
|
54
|
-
try:
|
|
55
|
-
res = subprocess.run(cmd, cwd=str(cwd) if cwd else None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
|
|
56
|
-
return res.stdout.strip() if res.returncode == 0 else ""
|
|
57
|
-
except Exception:
|
|
58
|
-
return ""
|
|
59
|
-
|
|
60
|
-
def scan_repo(repo_path: Path) -> Dict[str, Any]:
|
|
61
|
-
repo_path = repo_path.resolve()
|
|
62
|
-
facts: Dict[str, Any] = {
|
|
63
|
-
"repo_name": repo_path.name,
|
|
64
|
-
"repo_path": str(repo_path),
|
|
65
|
-
"detected_languages": {},
|
|
66
|
-
"detected_tech_stacks": set(),
|
|
67
|
-
"architectural_signals": [],
|
|
68
|
-
"author_info": {},
|
|
69
|
-
"git_stats": {},
|
|
70
|
-
"key_dependencies": [],
|
|
71
|
-
"evidence_items": []
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if not repo_path.exists() or not repo_path.is_dir():
|
|
75
|
-
return facts
|
|
76
|
-
|
|
77
|
-
# 1. Git 信息提取 (Author, commits, tags)
|
|
78
|
-
git_dir = repo_path / ".git"
|
|
79
|
-
if git_dir.exists():
|
|
80
|
-
user_name = run_cmd(["git", "config", "user.name"], cwd=repo_path)
|
|
81
|
-
user_email = run_cmd(["git", "config", "user.email"], cwd=repo_path)
|
|
82
|
-
commit_count = run_cmd(["git", "rev-list", "--count", "HEAD"], cwd=repo_path)
|
|
83
|
-
remote_url = run_cmd(["git", "config", "--get", "remote.origin.url"], cwd=repo_path)
|
|
84
|
-
recent_commits = run_cmd(["git", "log", "-n", "8", "--pretty=format:%s"], cwd=repo_path)
|
|
85
|
-
|
|
86
|
-
facts["author_info"]["name"] = user_name or ""
|
|
87
|
-
facts["author_info"]["email"] = user_email or ""
|
|
88
|
-
facts["author_info"]["git_remote"] = remote_url or ""
|
|
89
|
-
facts["git_stats"]["total_commits"] = int(commit_count) if commit_count.isdigit() else 0
|
|
90
|
-
facts["git_stats"]["recent_commits"] = [c for c in recent_commits.splitlines() if c]
|
|
91
|
-
|
|
92
|
-
# 2. 统计文件类型分布
|
|
93
|
-
lang_map = {
|
|
94
|
-
".ts": "TypeScript", ".tsx": "TypeScript/React", ".js": "JavaScript", ".jsx": "JavaScript/React",
|
|
95
|
-
".py": "Python", ".go": "Go", ".rs": "Rust", ".java": "Java", ".cpp": "C++", ".c": "C",
|
|
96
|
-
".html": "HTML", ".css": "CSS", ".vue": "Vue", ".sql": "SQL", ".sh": "Shell"
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
file_counts = {}
|
|
100
|
-
ignored_dirs = {".git", "node_modules", ".venv", "venv", "__pycache__", "dist", "build", ".next", ".turbo"}
|
|
101
|
-
|
|
102
|
-
for root, dirs, files in os.walk(repo_path):
|
|
103
|
-
dirs[:] = [d for d in dirs if d not in ignored_dirs and not d.startswith(".")]
|
|
104
|
-
for f in files:
|
|
105
|
-
p = Path(root) / f
|
|
106
|
-
ext = p.suffix.lower()
|
|
107
|
-
if ext in lang_map:
|
|
108
|
-
lang = lang_map[ext]
|
|
109
|
-
file_counts[lang] = file_counts.get(lang, 0) + 1
|
|
110
|
-
|
|
111
|
-
facts["detected_languages"] = file_counts
|
|
112
|
-
|
|
113
|
-
# 3. 依赖文件深度解析
|
|
114
|
-
dep_names = set()
|
|
115
|
-
|
|
116
|
-
# 3.1 Node/TS
|
|
117
|
-
pkg_json = repo_path / "package.json"
|
|
118
|
-
if pkg_json.exists():
|
|
119
|
-
try:
|
|
120
|
-
pkg_data = json.loads(pkg_json.read_text(encoding="utf-8"))
|
|
121
|
-
if not facts["author_info"].get("name") and pkg_data.get("author"):
|
|
122
|
-
author_val = pkg_data.get("author")
|
|
123
|
-
facts["author_info"]["name"] = author_val if isinstance(author_val, str) else author_val.get("name", "")
|
|
124
|
-
if not facts["author_info"].get("git_remote") and pkg_data.get("repository"):
|
|
125
|
-
repo_val = pkg_data.get("repository")
|
|
126
|
-
facts["author_info"]["git_remote"] = repo_val if isinstance(repo_val, str) else repo_val.get("url", "")
|
|
127
|
-
|
|
128
|
-
deps = {**pkg_data.get("dependencies", {}), **pkg_data.get("devDependencies", {})}
|
|
129
|
-
dep_names.update(deps.keys())
|
|
130
|
-
except Exception:
|
|
131
|
-
pass
|
|
132
|
-
|
|
133
|
-
# 3.2 Python
|
|
134
|
-
req_txt = repo_path / "requirements.txt"
|
|
135
|
-
if req_txt.exists():
|
|
136
|
-
try:
|
|
137
|
-
for line in req_txt.read_text(encoding="utf-8").splitlines():
|
|
138
|
-
line = line.strip()
|
|
139
|
-
if line and not line.startswith("#"):
|
|
140
|
-
dep_names.add(re.split(r'[=<>~]', line)[0].strip().lower())
|
|
141
|
-
except Exception:
|
|
142
|
-
pass
|
|
143
|
-
|
|
144
|
-
pyproject = repo_path / "pyproject.toml"
|
|
145
|
-
if pyproject.exists():
|
|
146
|
-
try:
|
|
147
|
-
content = pyproject.read_text(encoding="utf-8").lower()
|
|
148
|
-
for key in ["fastapi", "torch", "langchain", "qdrant", "pytest", "pydantic"]:
|
|
149
|
-
if key in content:
|
|
150
|
-
dep_names.add(key)
|
|
151
|
-
except Exception:
|
|
152
|
-
pass
|
|
153
|
-
|
|
154
|
-
# 3.3 Go
|
|
155
|
-
gomod = repo_path / "go.mod"
|
|
156
|
-
if gomod.exists():
|
|
157
|
-
try:
|
|
158
|
-
for line in gomod.read_text(encoding="utf-8").splitlines():
|
|
159
|
-
line = line.strip()
|
|
160
|
-
if line.startswith("require") or " " in line:
|
|
161
|
-
parts = line.split()
|
|
162
|
-
if len(parts) >= 2 and "/" in parts[0]:
|
|
163
|
-
dep_names.add(parts[0])
|
|
164
|
-
except Exception:
|
|
165
|
-
pass
|
|
166
|
-
|
|
167
|
-
# 3.4 Rust
|
|
168
|
-
cargo = repo_path / "Cargo.toml"
|
|
169
|
-
if cargo.exists():
|
|
170
|
-
try:
|
|
171
|
-
for line in cargo.read_text(encoding="utf-8").splitlines():
|
|
172
|
-
line = line.strip()
|
|
173
|
-
if "=" in line and not line.startswith("["):
|
|
174
|
-
dep_names.add(line.split("=")[0].strip().lower())
|
|
175
|
-
except Exception:
|
|
176
|
-
pass
|
|
177
|
-
|
|
178
|
-
# 4. 匹配技术栈标签
|
|
179
|
-
for tech, sigs in TECH_SIGNATURES.items():
|
|
180
|
-
for sig in sigs:
|
|
181
|
-
if sig.lower() in [d.lower() for d in dep_names] or (repo_path / sig).exists():
|
|
182
|
-
facts["detected_tech_stacks"].add(tech)
|
|
183
|
-
break
|
|
184
|
-
|
|
185
|
-
# 5. 架构与工程化信号
|
|
186
|
-
if (repo_path / "packages").exists() or (repo_path / "pnpm-workspace.yaml").exists():
|
|
187
|
-
facts["architectural_signals"].append("Monorepo 模块化架构")
|
|
188
|
-
if (repo_path / "Dockerfile").exists() or (repo_path / "docker-compose.yml").exists():
|
|
189
|
-
facts["architectural_signals"].append("Docker 容器化交付")
|
|
190
|
-
if (repo_path / ".github" / "workflows").exists():
|
|
191
|
-
facts["architectural_signals"].append("CI/CD 自动化流水线")
|
|
192
|
-
if (repo_path / "tests").exists() or (repo_path / "test").exists():
|
|
193
|
-
facts["architectural_signals"].append("自动化单元/集成测试")
|
|
194
|
-
|
|
195
|
-
facts["key_dependencies"] = sorted(list(dep_names))
|
|
196
|
-
facts["detected_tech_stacks"] = sorted(list(facts["detected_tech_stacks"]))
|
|
197
|
-
|
|
198
|
-
# 6. 生成 L1~L3 证据项
|
|
199
|
-
evidence_items = []
|
|
200
|
-
if facts["detected_tech_stacks"]:
|
|
201
|
-
evidence_items.append({
|
|
202
|
-
"claim": f"基于 {', '.join(facts['detected_tech_stacks'][:4])} 构建核心工程架构,实现高可靠端到端业务流",
|
|
203
|
-
"evidenceLevel": "L1",
|
|
204
|
-
"evidenceSource": f"代码库依赖配置文件 ({', '.join([f for f in ['package.json', 'go.mod', 'requirements.txt', 'Cargo.toml'] if (repo_path / f).exists()]) or '依赖树'})",
|
|
205
|
-
"keywords": facts["detected_tech_stacks"][:5]
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
if facts["architectural_signals"]:
|
|
209
|
-
evidence_items.append({
|
|
210
|
-
"claim": f"主导设计工程架构体系,落地 {', '.join(facts['architectural_signals'])},保障交付效率与质量",
|
|
211
|
-
"evidenceLevel": "L1",
|
|
212
|
-
"evidenceSource": "工程根目录架构配置文件与流水线定义",
|
|
213
|
-
"keywords": ["Architecture", "CI/CD", "Testing", "DevOps"]
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
if facts["git_stats"].get("total_commits", 0) > 10:
|
|
217
|
-
evidence_items.append({
|
|
218
|
-
"claim": f"持续推进工程重构与版本迭代,累计提交 {facts['git_stats']['total_commits']} 次代码并完成多版本发布",
|
|
219
|
-
"evidenceLevel": "L2",
|
|
220
|
-
"evidenceSource": "Git 提交历史与版本变更记录",
|
|
221
|
-
"keywords": ["Refactoring", "Git", "Release Engineering"]
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
facts["evidence_items"] = evidence_items
|
|
225
|
-
return facts
|
|
226
|
-
|
|
227
|
-
def build_master_profile(facts: Dict[str, Any], user_overrides: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
228
|
-
"""Build structured candidate profile from codebase facts + optional user overrides.
|
|
229
|
-
|
|
230
|
-
Placeholder discipline:
|
|
231
|
-
- repo_mode=True : facts dict is populated from real repo scan; safe to synthesize
|
|
232
|
-
experience/education skeletons because they'll be user-reviewed before use.
|
|
233
|
-
- repo_mode=False: only user_overrides carry real data. NEVER emit fabricated placeholder
|
|
234
|
-
strings (fake company, fake school, fake dates). Use null / empty list instead.
|
|
235
|
-
"""
|
|
236
|
-
overrides = user_overrides or {}
|
|
237
|
-
author = facts.get("author_info", {})
|
|
238
|
-
repo_mode: bool = bool(facts.get("detected_tech_stacks") or facts.get("evidence_items"))
|
|
239
|
-
|
|
240
|
-
# ── Basics ────────────────────────────────────────────────────────────────
|
|
241
|
-
name = overrides.get("name") or author.get("name") or (None if not repo_mode else "候选人")
|
|
242
|
-
title = overrides.get("title") or (None if not repo_mode else "资深工程师 / 技术专家")
|
|
243
|
-
email = overrides.get("email") or author.get("email") or (None if not repo_mode else "candidate@example.com")
|
|
244
|
-
phone = overrides.get("phone") or (None if not repo_mode else None)
|
|
245
|
-
github = overrides.get("github") or author.get("git_remote") or None
|
|
246
|
-
location = overrides.get("location") or (None if not repo_mode else None)
|
|
247
|
-
|
|
248
|
-
tech_stacks = facts.get("detected_tech_stacks", [])
|
|
249
|
-
languages = list(facts.get("detected_languages", {}).keys())
|
|
250
|
-
|
|
251
|
-
# ── Skills ────────────────────────────────────────────────────────────────
|
|
252
|
-
all_tech = tech_stacks + [l for l in languages if l not in tech_stacks]
|
|
253
|
-
arch_signals = facts.get("architectural_signals", [])
|
|
254
|
-
skills: list = []
|
|
255
|
-
if all_tech:
|
|
256
|
-
skills.append({"category": "核心技术栈", "items": all_tech, "highlighted": all_tech[:4]})
|
|
257
|
-
if arch_signals:
|
|
258
|
-
skills.append({"category": "工程化与架构", "items": arch_signals, "highlighted": arch_signals[:2]})
|
|
259
|
-
|
|
260
|
-
# ── Experience ────────────────────────────────────────────────────────────
|
|
261
|
-
bullets = [
|
|
262
|
-
{
|
|
263
|
-
"text": item["claim"],
|
|
264
|
-
"evidenceLevel": item["evidenceLevel"],
|
|
265
|
-
"evidenceSource": item["evidenceSource"],
|
|
266
|
-
"keywords": item.get("keywords", [])
|
|
267
|
-
}
|
|
268
|
-
for item in facts.get("evidence_items", [])
|
|
269
|
-
]
|
|
270
|
-
|
|
271
|
-
if repo_mode and bullets:
|
|
272
|
-
# Repo mode: synthesize a single experience entry from code evidence.
|
|
273
|
-
# Company / dates are placeholders that the user MUST review.
|
|
274
|
-
experience = [
|
|
275
|
-
{
|
|
276
|
-
"company": overrides.get("company") or "[请填写:公司名称]",
|
|
277
|
-
"role": title or "[请填写:职位名称]",
|
|
278
|
-
"startDate": overrides.get("startDate") or "[请填写:开始时间]",
|
|
279
|
-
"endDate": overrides.get("endDate") or "至今",
|
|
280
|
-
"location": location or "[请填写:工作地点]",
|
|
281
|
-
"summary": "负责核心产品研发与关键架构攻坚",
|
|
282
|
-
"bullets": bullets
|
|
283
|
-
}
|
|
284
|
-
]
|
|
285
|
-
elif overrides.get("experience"):
|
|
286
|
-
experience = overrides["experience"]
|
|
287
|
-
else:
|
|
288
|
-
experience = []
|
|
289
|
-
|
|
290
|
-
# ── Projects ──────────────────────────────────────────────────────────────
|
|
291
|
-
if repo_mode and tech_stacks:
|
|
292
|
-
projects = [
|
|
293
|
-
{
|
|
294
|
-
"name": facts.get("repo_name") or "[请填写:项目名称]",
|
|
295
|
-
"role": "核心负责人 / 主力开发者",
|
|
296
|
-
"techStack": tech_stacks[:5],
|
|
297
|
-
"repoUrl": github,
|
|
298
|
-
"startDate": "[请填写:开始时间]",
|
|
299
|
-
"endDate": "至今",
|
|
300
|
-
"bullets": [
|
|
301
|
-
{"text": item["claim"], "evidenceLevel": item["evidenceLevel"], "evidenceSource": item["evidenceSource"]}
|
|
302
|
-
for item in facts.get("evidence_items", [])
|
|
303
|
-
]
|
|
304
|
-
}
|
|
305
|
-
]
|
|
306
|
-
elif overrides.get("projects"):
|
|
307
|
-
projects = overrides["projects"]
|
|
308
|
-
else:
|
|
309
|
-
projects = []
|
|
310
|
-
|
|
311
|
-
# ── Education ─────────────────────────────────────────────────────────────
|
|
312
|
-
if overrides.get("education"):
|
|
313
|
-
education = overrides["education"]
|
|
314
|
-
elif overrides.get("school") or overrides.get("degree"):
|
|
315
|
-
education = [
|
|
316
|
-
{
|
|
317
|
-
"institution": overrides.get("school") or "[请填写:学校名称]",
|
|
318
|
-
"degree": overrides.get("degree") or "[请填写:学历与专业]",
|
|
319
|
-
"startDate": overrides.get("edu_start") or "[请填写:入学时间]",
|
|
320
|
-
"endDate": overrides.get("edu_end") or "[请填写:毕业时间]"
|
|
321
|
-
}
|
|
322
|
-
]
|
|
323
|
-
elif repo_mode:
|
|
324
|
-
# Repo mode: emit a reminder skeleton so the user knows to fill it in.
|
|
325
|
-
education = [
|
|
326
|
-
{
|
|
327
|
-
"institution": "[请填写:学校名称]",
|
|
328
|
-
"degree": "[请填写:学历与专业]",
|
|
329
|
-
"startDate": "[请填写:入学时间]",
|
|
330
|
-
"endDate": "[请填写:毕业时间]"
|
|
331
|
-
}
|
|
332
|
-
]
|
|
333
|
-
else:
|
|
334
|
-
education = []
|
|
335
|
-
|
|
336
|
-
# ── Assemble ──────────────────────────────────────────────────────────────
|
|
337
|
-
basics: Dict[str, Any] = {}
|
|
338
|
-
if name: basics["name"] = name
|
|
339
|
-
if title: basics["title"] = title
|
|
340
|
-
if email: basics["email"] = email
|
|
341
|
-
if phone: basics["phone"] = phone
|
|
342
|
-
if location: basics["location"] = location
|
|
343
|
-
if github: basics["github"] = github
|
|
344
|
-
if all_tech:
|
|
345
|
-
basics["summary"] = (
|
|
346
|
-
f"拥有坚实的技术研发与工程化落地实战经验,"
|
|
347
|
-
f"主导过多个基于 {', '.join(all_tech[:3])} 的核心系统设计与交付。"
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
profile: Dict[str, Any] = {"basics": basics}
|
|
351
|
-
if skills: profile["skills"] = skills
|
|
352
|
-
if experience: profile["experience"] = experience
|
|
353
|
-
if projects: profile["projects"] = projects
|
|
354
|
-
if education: profile["education"] = education
|
|
355
|
-
return profile
|
|
356
6
|
|
|
357
7
|
def main():
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
overrides = {}
|
|
373
|
-
if args.name: overrides["name"] = args.name
|
|
374
|
-
if args.role: overrides["title"] = args.role
|
|
375
|
-
if args.email: overrides["email"] = args.email
|
|
376
|
-
if args.phone: overrides["phone"] = args.phone
|
|
377
|
-
|
|
378
|
-
profile = build_master_profile(facts, overrides)
|
|
379
|
-
|
|
380
|
-
out_path = Path(args.output)
|
|
381
|
-
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
382
|
-
out_path.write_text(json.dumps(profile, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
383
|
-
|
|
384
|
-
if not args.quiet:
|
|
385
|
-
print("==============================================================")
|
|
386
|
-
print(" KnowMe CareerForge — Codebase Evidence Mining Report")
|
|
387
|
-
print(f" Target Repository : {facts['repo_path']}")
|
|
388
|
-
print(f" Detected Techs : {', '.join(facts['detected_tech_stacks']) or 'None'}")
|
|
389
|
-
print(f" Architecture : {', '.join(facts['architectural_signals']) or 'Standard'}")
|
|
390
|
-
print(f" Evidence Generated: {len(facts['evidence_items'])} L1/L2 items")
|
|
391
|
-
print(f" Profile Written To: {out_path}")
|
|
392
|
-
print("==============================================================")
|
|
393
|
-
|
|
394
|
-
if __name__ == "__main__":
|
|
395
|
-
main()
|
|
8
|
+
# Intentionally do not parse repository/output paths or read candidate inputs.
|
|
9
|
+
# Old CLI/npm/direct callers must fail before creating or overwriting files.
|
|
10
|
+
print(json.dumps({
|
|
11
|
+
'status': 'FAIL',
|
|
12
|
+
'stage': 'unsupported',
|
|
13
|
+
'errors': ['Repository-to-resume extraction is not supported. Use user-provided facts with forge --profile-json.'],
|
|
14
|
+
'warnings': [],
|
|
15
|
+
'outputs': {},
|
|
16
|
+
}))
|
|
17
|
+
return 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == '__main__':
|
|
21
|
+
sys.exit(main())
|