knowme-careerforge-skill 0.0.2 → 0.0.4
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/SKILL.md +11 -1
- package/agents/claude/knowme-careerforge.md +10 -9
- package/agents/codex/knowme-careerforge.yaml +3 -3
- package/agents/cursor/knowme-careerforge.mdc +12 -8
- package/agents/opencode/skill.yaml +1 -1
- package/agents/windsurf/knowme-careerforge.rules +9 -9
- package/dist/cli/src/commands/init.js +9 -0
- package/dist/cli/src/index.js +17 -0
- package/package.json +2 -1
- package/scripts/build/sync-version.py +11 -2
- package/scripts/template/instantiate-resume.py +8 -2
- package/scripts/template/search-template.py +290 -89
- package/skill.json +1 -1
- package/src/knowledge/index.json +2 -10
- package/src/knowledge/templates.json +1 -9
- package/src/references/01-evidence-mining.md +41 -0
- package/src/references/02-career-goal.md +38 -0
- package/src/references/03-jd-analysis.md +44 -0
- package/src/references/04-template-selection.md +43 -0
- package/src/references/05-html-canvas-tokens.md +51 -0
- package/src/references/06-qa-and-rendering.md +54 -0
- package/src/templates/classic/style.css +22 -51
- package/src/templates/common/base.css +150 -0
- package/src/templates/executive/style.css +30 -73
- package/src/templates/minimal/style.css +30 -72
- package/src/templates/modern/style.css +32 -72
- package/src/references/ats.md +0 -17
- package/src/references/evidence-first.md +0 -22
- package/src/references/pdf.md +0 -40
- package/src/references/resume-writing.md +0 -25
- package/src/references/template-design.md +0 -57
- package/src/workflows/analyze-job.md +0 -16
- package/src/workflows/define-goal.md +0 -16
- package/src/workflows/forge-resume.md +0 -25
- package/src/workflows/know-me.md +0 -21
- package/src/workflows/position.md +0 -19
- package/src/workflows/review.md +0 -31
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
KnowMe CareerForge — Template Search & Ranking Engine
|
|
4
|
-
|
|
3
|
+
KnowMe CareerForge — Template Search & Ranking Engine
|
|
4
|
+
Architecture: Deep Module with Pluggable Scorer Strategies (Weighted Rules + BM25 Text Search)
|
|
5
|
+
|
|
6
|
+
Design Contract:
|
|
7
|
+
- BaseTemplateScorer: Abstract strategy interface
|
|
8
|
+
- WeightedRuleScorer: Multi-criteria rule-based weighted scorer (Role 35%, Style 25%, ATS 20%, Page 10%, Density 10%)
|
|
9
|
+
- BM25TextScorer: Pure-Python BM25 keyword retrieval across template text documents
|
|
10
|
+
- HybridTemplateScorer: Weighted blend (70% Rule Score + 30% BM25 Text Score)
|
|
11
|
+
- search_templates(): Public facade interface
|
|
5
12
|
"""
|
|
6
13
|
|
|
7
|
-
import sys, os, json, argparse
|
|
14
|
+
import sys, os, json, argparse, math, re
|
|
8
15
|
from pathlib import Path
|
|
16
|
+
from dataclasses import dataclass, field
|
|
9
17
|
from typing import List, Dict, Any, Optional
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
|
|
10
20
|
|
|
11
21
|
def get_project_root() -> Path:
|
|
12
22
|
curr = Path(__file__).resolve().parent
|
|
@@ -16,6 +26,17 @@ def get_project_root() -> Path:
|
|
|
16
26
|
curr = curr.parent
|
|
17
27
|
return Path.cwd()
|
|
18
28
|
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class SearchQuery:
|
|
32
|
+
role: str
|
|
33
|
+
style: Optional[str] = None
|
|
34
|
+
target_pages: int = 1
|
|
35
|
+
density: str = "balanced"
|
|
36
|
+
keywords: Optional[str] = None
|
|
37
|
+
engine: str = "hybrid" # "weighted" | "bm25" | "hybrid"
|
|
38
|
+
|
|
39
|
+
|
|
19
40
|
def load_templates() -> List[Dict[str, Any]]:
|
|
20
41
|
root_dir = get_project_root()
|
|
21
42
|
base_dir = root_dir / "src" / "templates"
|
|
@@ -30,120 +51,300 @@ def load_templates() -> List[Dict[str, Any]]:
|
|
|
30
51
|
if meta_file.exists():
|
|
31
52
|
try:
|
|
32
53
|
data = json.loads(meta_file.read_text(encoding="utf-8"))
|
|
33
|
-
data["
|
|
54
|
+
data["_directory"] = str(t_dir.relative_to(root_dir))
|
|
34
55
|
templates.append(data)
|
|
35
56
|
except Exception:
|
|
36
57
|
pass
|
|
37
58
|
return templates
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
|
|
61
|
+
# ==============================================================================
|
|
62
|
+
# 1. SCORER STRATEGY INTERFACE & IMPLEMENTATIONS
|
|
63
|
+
# ==============================================================================
|
|
64
|
+
|
|
65
|
+
class BaseTemplateScorer(ABC):
|
|
66
|
+
"""Abstract interface for template scoring algorithms."""
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def score(self, template: Dict[str, Any], query: SearchQuery) -> float:
|
|
70
|
+
"""Return match score normalized between 0.0 and 100.0."""
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class WeightedRuleScorer(BaseTemplateScorer):
|
|
75
|
+
"""
|
|
76
|
+
Rule-based weighted scoring engine:
|
|
77
|
+
- Role Match: 35%
|
|
78
|
+
- Style Match: 25%
|
|
79
|
+
- ATS Tier: 20%
|
|
80
|
+
- Page Fit: 10%
|
|
81
|
+
- Density Fit: 10%
|
|
82
|
+
"""
|
|
46
83
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
elif "engineer" in query_lower or "architect" in query_lower:
|
|
59
|
-
role_score = 0.85
|
|
60
|
-
else:
|
|
61
|
-
role_score = 0.60
|
|
62
|
-
|
|
63
|
-
score += role_score * 0.35
|
|
64
|
-
|
|
65
|
-
# 2. 风格匹配度 (Style Match - 25%)
|
|
66
|
-
style_score = 0.70
|
|
67
|
-
if query_style:
|
|
68
|
-
q_style_lower = query_style.lower()
|
|
69
|
-
t_style = template.get("style", "").lower()
|
|
70
|
-
t_tone = template.get("visualStyle", {}).get("tone", "").lower()
|
|
84
|
+
def __init__(self, weights: Optional[Dict[str, float]] = None):
|
|
85
|
+
self.weights = weights or {
|
|
86
|
+
"role": 0.35,
|
|
87
|
+
"style": 0.25,
|
|
88
|
+
"ats": 0.20,
|
|
89
|
+
"page": 0.10,
|
|
90
|
+
"density": 0.10
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def score(self, template: Dict[str, Any], query: SearchQuery) -> float:
|
|
94
|
+
score = 0.0
|
|
71
95
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
# 1. Role Match
|
|
97
|
+
role_score = 0.0
|
|
98
|
+
query_lower = query.role.lower()
|
|
99
|
+
supported = [r.lower() for r in template.get("supportedRoles", [])]
|
|
100
|
+
|
|
101
|
+
for r in supported:
|
|
102
|
+
if r in query_lower or query_lower in r:
|
|
103
|
+
role_score = 1.0
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
if role_score < 0.5:
|
|
107
|
+
cat = template.get("roleCategory", "")
|
|
108
|
+
if any(k in query_lower for k in ["ai", "agent", "llm", "frontend", "backend", "fullstack", "devops", "engineer"]) and "engineering" in cat:
|
|
109
|
+
role_score = 0.85
|
|
110
|
+
elif any(k in query_lower for k in ["manager", "product", "lead", "director", "architect", "cto"]) and "management" in cat:
|
|
111
|
+
role_score = 0.85
|
|
112
|
+
elif any(k in query_lower for k in ["bank", "fintech", "state", "corp", "government"]) and "corporate" in cat:
|
|
113
|
+
role_score = 0.85
|
|
114
|
+
else:
|
|
115
|
+
role_score = 0.60
|
|
116
|
+
|
|
117
|
+
score += role_score * self.weights["role"]
|
|
118
|
+
|
|
119
|
+
# 2. Style Match
|
|
120
|
+
style_score = 0.70
|
|
121
|
+
if query.style:
|
|
122
|
+
q_style_lower = query.style.lower()
|
|
123
|
+
t_style = template.get("style", "").lower()
|
|
124
|
+
t_tone = template.get("visualStyle", {}).get("tone", "").lower()
|
|
125
|
+
|
|
126
|
+
if q_style_lower in t_style or q_style_lower in t_tone:
|
|
127
|
+
style_score = 1.0
|
|
128
|
+
elif any(k in t_style for k in q_style_lower.split("-")):
|
|
129
|
+
style_score = 0.85
|
|
130
|
+
else:
|
|
131
|
+
style_score = 0.50
|
|
132
|
+
score += style_score * self.weights["style"]
|
|
133
|
+
|
|
134
|
+
# 3. ATS Tier
|
|
135
|
+
ats_tier = template.get("atsScoreTier", "tier-1-optimal")
|
|
136
|
+
ats_score = 1.0 if "optimal" in ats_tier or "tier-1" in ats_tier else 0.85
|
|
137
|
+
score += ats_score * self.weights["ats"]
|
|
138
|
+
|
|
139
|
+
# 4. Page Fit
|
|
140
|
+
target_p = template.get("layout", {}).get("targetPages", 1)
|
|
141
|
+
max_p = template.get("layout", {}).get("maxPages", 2)
|
|
142
|
+
if query.target_pages == target_p:
|
|
143
|
+
page_score = 1.0
|
|
144
|
+
elif query.target_pages <= max_p:
|
|
145
|
+
page_score = 0.80
|
|
80
146
|
else:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
147
|
+
page_score = 0.50
|
|
148
|
+
score += page_score * self.weights["page"]
|
|
149
|
+
|
|
150
|
+
# 5. Density Preference
|
|
151
|
+
t_density = template.get("layout", {}).get("density", "balanced")
|
|
152
|
+
density_score = 1.0 if query.density == t_density else 0.80
|
|
153
|
+
score += density_score * self.weights["density"]
|
|
154
|
+
|
|
155
|
+
return round(score * 100, 1)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class BM25TextScorer(BaseTemplateScorer):
|
|
159
|
+
"""
|
|
160
|
+
Pure Python BM25 Text Scorer across template metadata documents.
|
|
161
|
+
Constructs a virtual document per template: (name, category, style, supportedRoles, tone).
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self, templates: Optional[List[Dict[str, Any]]] = None, k1: float = 1.5, b: float = 0.75):
|
|
165
|
+
self.k1 = k1
|
|
166
|
+
self.b = b
|
|
167
|
+
self.templates = templates or load_templates()
|
|
168
|
+
self.docs = [self._extract_doc(t) for t in self.templates]
|
|
169
|
+
self.avg_dl = sum(len(d) for d in self.docs) / max(len(self.docs), 1)
|
|
170
|
+
self.doc_freqs = self._calc_doc_freqs()
|
|
171
|
+
|
|
172
|
+
def _extract_doc(self, template: Dict[str, Any]) -> List[str]:
|
|
173
|
+
text_parts = [
|
|
174
|
+
template.get("name", ""),
|
|
175
|
+
template.get("style", ""),
|
|
176
|
+
template.get("roleCategory", ""),
|
|
177
|
+
template.get("visualStyle", {}).get("tone", ""),
|
|
178
|
+
" ".join(template.get("supportedRoles", []))
|
|
179
|
+
]
|
|
180
|
+
combined = " ".join(text_parts).lower()
|
|
181
|
+
# Tokenize words & Chinese characters
|
|
182
|
+
tokens = re.findall(r'[a-zA-Z0-9_\-]+|[\u4e00-\u9fa5]', combined)
|
|
183
|
+
return tokens
|
|
99
184
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
185
|
+
def _calc_doc_freqs(self) -> Dict[str, int]:
|
|
186
|
+
freqs: Dict[str, int] = {}
|
|
187
|
+
for doc in self.docs:
|
|
188
|
+
for word in set(doc):
|
|
189
|
+
freqs[word] = freqs.get(word, 0) + 1
|
|
190
|
+
return freqs
|
|
104
191
|
|
|
105
|
-
|
|
192
|
+
def score(self, template: Dict[str, Any], query: SearchQuery) -> float:
|
|
193
|
+
doc = self._extract_doc(template)
|
|
194
|
+
doc_len = len(doc)
|
|
195
|
+
if doc_len == 0:
|
|
196
|
+
return 50.0
|
|
106
197
|
|
|
107
|
-
|
|
198
|
+
query_text = f"{query.role} {query.style or ''} {query.keywords or ''}".lower()
|
|
199
|
+
q_tokens = re.findall(r'[a-zA-Z0-9_\-]+|[\u4e00-\u9fa5]', query_text)
|
|
200
|
+
if not q_tokens:
|
|
201
|
+
return 70.0
|
|
202
|
+
|
|
203
|
+
n_docs = len(self.docs)
|
|
204
|
+
score = 0.0
|
|
205
|
+
|
|
206
|
+
for term in q_tokens:
|
|
207
|
+
df = self.doc_freqs.get(term, 0)
|
|
208
|
+
if df == 0:
|
|
209
|
+
continue
|
|
210
|
+
# Standard BM25 IDF with smoothing
|
|
211
|
+
idf = math.log(1 + (n_docs - df + 0.5) / (df + 0.5))
|
|
212
|
+
tf = doc.count(term)
|
|
213
|
+
numerator = tf * (self.k1 + 1)
|
|
214
|
+
denominator = tf + self.k1 * (1 - self.b + self.b * (doc_len / max(self.avg_dl, 1)))
|
|
215
|
+
score += idf * (numerator / max(denominator, 0.001))
|
|
216
|
+
|
|
217
|
+
# Normalize score into roughly 50.0 ~ 100.0 range
|
|
218
|
+
normalized = 50.0 + min(score * 15.0, 50.0)
|
|
219
|
+
return round(normalized, 1)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class HybridTemplateScorer(BaseTemplateScorer):
|
|
223
|
+
"""
|
|
224
|
+
Weighted combination of rule-based constraints and BM25 text relevance.
|
|
225
|
+
Default: 70% Weighted Rules + 30% BM25 Relevance.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
def __init__(self, rule_weight: float = 0.70, bm25_weight: float = 0.30):
|
|
229
|
+
self.rule_scorer = WeightedRuleScorer()
|
|
230
|
+
self.bm25_scorer = BM25TextScorer()
|
|
231
|
+
self.rule_weight = rule_weight
|
|
232
|
+
self.bm25_weight = bm25_weight
|
|
233
|
+
|
|
234
|
+
def score(self, template: Dict[str, Any], query: SearchQuery) -> float:
|
|
235
|
+
rule_score = self.rule_scorer.score(template, query)
|
|
236
|
+
bm25_score = self.bm25_scorer.score(template, query)
|
|
237
|
+
blended = (rule_score * self.rule_weight) + (bm25_score * self.bm25_weight)
|
|
238
|
+
return round(blended, 1)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ==============================================================================
|
|
242
|
+
# 2. PUBLIC FACADE INTERFACE (DEEP MODULE)
|
|
243
|
+
# ==============================================================================
|
|
244
|
+
|
|
245
|
+
def search_templates(
|
|
246
|
+
role: str,
|
|
247
|
+
style: Optional[str] = None,
|
|
248
|
+
target_pages: int = 1,
|
|
249
|
+
density: str = "balanced",
|
|
250
|
+
keywords: Optional[str] = None,
|
|
251
|
+
engine: str = "hybrid"
|
|
252
|
+
) -> List[Dict[str, Any]]:
|
|
253
|
+
"""
|
|
254
|
+
Search and rank HTML resume templates.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
role: Target job title or direction (e.g., 'AI Agent Engineer', 'Tech Lead')
|
|
258
|
+
style: Preferred visual/layout style (e.g., 'single-column-minimal', 'two-column-split')
|
|
259
|
+
target_pages: Expected page count (1 or 2)
|
|
260
|
+
density: Information density preference ('high', 'balanced', 'spacious')
|
|
261
|
+
keywords: Optional JD keywords for text matching
|
|
262
|
+
engine: Scoring engine ('hybrid' | 'weighted' | 'bm25')
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
List of matching templates sorted by matchScore descending.
|
|
266
|
+
"""
|
|
267
|
+
query = SearchQuery(
|
|
268
|
+
role=role,
|
|
269
|
+
style=style,
|
|
270
|
+
target_pages=target_pages,
|
|
271
|
+
density=density,
|
|
272
|
+
keywords=keywords,
|
|
273
|
+
engine=engine
|
|
274
|
+
)
|
|
275
|
+
|
|
108
276
|
templates = load_templates()
|
|
277
|
+
if not templates:
|
|
278
|
+
return []
|
|
279
|
+
|
|
280
|
+
# Select Scorer Strategy
|
|
281
|
+
scorer: BaseTemplateScorer
|
|
282
|
+
if engine == "bm25":
|
|
283
|
+
scorer = BM25TextScorer(templates)
|
|
284
|
+
elif engine == "weighted":
|
|
285
|
+
scorer = WeightedRuleScorer()
|
|
286
|
+
else:
|
|
287
|
+
scorer = HybridTemplateScorer()
|
|
288
|
+
|
|
109
289
|
results = []
|
|
110
|
-
|
|
111
290
|
for t in templates:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
291
|
+
t_copy = dict(t)
|
|
292
|
+
score_val = scorer.score(t, query)
|
|
293
|
+
t_copy["matchScore"] = score_val
|
|
294
|
+
t_copy["score"] = score_val
|
|
295
|
+
t_copy["template"] = t
|
|
296
|
+
results.append(t_copy)
|
|
117
297
|
|
|
118
|
-
results.sort(key=lambda x: x
|
|
298
|
+
results.sort(key=lambda x: x.get("matchScore", 0.0), reverse=True)
|
|
119
299
|
return results
|
|
120
|
-
|
|
121
300
|
def main():
|
|
122
301
|
parser = argparse.ArgumentParser(description="KnowMe CareerForge — Template Search Engine")
|
|
123
|
-
parser.add_argument("role", nargs="?", default=
|
|
124
|
-
parser.add_argument("--
|
|
125
|
-
parser.add_argument("--
|
|
126
|
-
parser.add_argument("--
|
|
127
|
-
parser.add_argument("--
|
|
128
|
-
|
|
302
|
+
parser.add_argument("role", nargs="?", default=None, help="Target role or job title (e.g. 'AI Agent Engineer')")
|
|
303
|
+
parser.add_argument("--role", "-r", dest="named_role", default=None, help="Target role (named option)")
|
|
304
|
+
parser.add_argument("--style", "-s", default=None, help="Style preference (minimal, modern, executive, classic)")
|
|
305
|
+
parser.add_argument("--target-pages", "-p", type=int, default=1, help="Target pages (1 or 2)")
|
|
306
|
+
parser.add_argument("--density", "-d", default="balanced", choices=["high", "balanced", "spacious"], help="Density")
|
|
307
|
+
parser.add_argument("--keywords", "-k", default=None, help="Additional JD keywords")
|
|
308
|
+
parser.add_argument("--engine", "-e", default="hybrid", choices=["hybrid", "weighted", "bm25"], help="Scoring engine")
|
|
309
|
+
parser.add_argument("--json", action="store_true", help="Output raw JSON array")
|
|
310
|
+
|
|
129
311
|
args = parser.parse_args()
|
|
130
|
-
|
|
312
|
+
target_role = args.named_role or args.role
|
|
313
|
+
if not target_role:
|
|
314
|
+
parser.error("the following arguments are required: role (or --role / -r)")
|
|
131
315
|
|
|
316
|
+
results = search_templates(
|
|
317
|
+
role=target_role,
|
|
318
|
+
style=args.style,
|
|
319
|
+
target_pages=args.target_pages,
|
|
320
|
+
density=args.density,
|
|
321
|
+
keywords=args.keywords,
|
|
322
|
+
engine=args.engine
|
|
323
|
+
)
|
|
324
|
+
|
|
132
325
|
if args.json:
|
|
133
326
|
print(json.dumps(results, ensure_ascii=False, indent=2))
|
|
134
327
|
return
|
|
135
|
-
|
|
328
|
+
|
|
136
329
|
print("=" * 80)
|
|
137
|
-
print(f" KnowMe CareerForge — Template
|
|
330
|
+
print(f" KnowMe CareerForge — Template Search Engine Results")
|
|
331
|
+
print(f" Query Role : {target_role}")
|
|
332
|
+
print(f" Style Filter : {args.style or 'Any'}")
|
|
333
|
+
print(f" Engine : {args.engine.upper()}")
|
|
334
|
+
print(f" Matches : {len(results)} template(s) found")
|
|
138
335
|
print("=" * 80)
|
|
336
|
+
|
|
139
337
|
for idx, r in enumerate(results, 1):
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
print(f"
|
|
143
|
-
print(f"
|
|
144
|
-
print(f"
|
|
145
|
-
print(f" Directory: {
|
|
338
|
+
custom_tokens = r.get("customizableTokens", [])
|
|
339
|
+
tokens_preview = ", ".join(custom_tokens[:4]) + ("..." if len(custom_tokens) > 4 else "")
|
|
340
|
+
print(f"\n{idx}. [{r.get('matchScore')} pts] {r.get('name')} (ID: {r.get('id')})")
|
|
341
|
+
print(f" Category: {r.get('roleCategory')} | Style: {r.get('style')} | ATS Tier: {r.get('atsScoreTier')}")
|
|
342
|
+
print(f" Target Pages: {r.get('layout', {}).get('targetPages', 1)} | Density: {r.get('layout', {}).get('density')}")
|
|
343
|
+
print(f" Directory: {r.get('_directory')}")
|
|
344
|
+
if tokens_preview:
|
|
345
|
+
print(f" Tokens : {tokens_preview}")
|
|
146
346
|
print("-" * 80)
|
|
147
347
|
|
|
348
|
+
|
|
148
349
|
if __name__ == "__main__":
|
|
149
350
|
main()
|
package/skill.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/skill.json",
|
|
3
3
|
"name": "knowme-careerforge",
|
|
4
4
|
"displayName": "KnowMe CareerForge",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.4",
|
|
6
6
|
"description": "An agent-native skill for self-discovery, career positioning, and tailored resume engineering. Know yourself. Define your direction. Forge your opportunity.",
|
|
7
7
|
"author": "Miles <https://github.com/Max-Samson>",
|
|
8
8
|
"license": "MIT",
|
package/src/knowledge/index.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.4",
|
|
3
3
|
"totalRoles": 9,
|
|
4
4
|
"totalTemplates": 4,
|
|
5
5
|
"roles": [
|
|
@@ -530,9 +530,7 @@
|
|
|
530
530
|
"--resume-font-size-meta",
|
|
531
531
|
"--resume-font-size-title",
|
|
532
532
|
"--resume-line-height-body",
|
|
533
|
-
"--resume-page-
|
|
534
|
-
"--resume-page-padding",
|
|
535
|
-
"--resume-page-width"
|
|
533
|
+
"--resume-page-padding"
|
|
536
534
|
]
|
|
537
535
|
},
|
|
538
536
|
{
|
|
@@ -592,8 +590,6 @@
|
|
|
592
590
|
"--resume-font-size-title",
|
|
593
591
|
"--resume-left-col-width",
|
|
594
592
|
"--resume-line-height-body",
|
|
595
|
-
"--resume-page-min-height",
|
|
596
|
-
"--resume-page-width",
|
|
597
593
|
"--resume-space-bullet",
|
|
598
594
|
"--resume-space-item",
|
|
599
595
|
"--resume-space-section"
|
|
@@ -654,9 +650,7 @@
|
|
|
654
650
|
"--resume-font-size-name",
|
|
655
651
|
"--resume-font-size-target",
|
|
656
652
|
"--resume-line-height-body",
|
|
657
|
-
"--resume-page-min-height",
|
|
658
653
|
"--resume-page-padding",
|
|
659
|
-
"--resume-page-width",
|
|
660
654
|
"--resume-space-bullet",
|
|
661
655
|
"--resume-space-header-bottom",
|
|
662
656
|
"--resume-space-item",
|
|
@@ -722,8 +716,6 @@
|
|
|
722
716
|
"--resume-font-size-name",
|
|
723
717
|
"--resume-font-size-target",
|
|
724
718
|
"--resume-line-height-body",
|
|
725
|
-
"--resume-page-min-height",
|
|
726
|
-
"--resume-page-width",
|
|
727
719
|
"--resume-sidebar-width",
|
|
728
720
|
"--resume-space-bullet",
|
|
729
721
|
"--resume-space-item",
|
|
@@ -51,9 +51,7 @@
|
|
|
51
51
|
"--resume-font-size-meta",
|
|
52
52
|
"--resume-font-size-title",
|
|
53
53
|
"--resume-line-height-body",
|
|
54
|
-
"--resume-page-
|
|
55
|
-
"--resume-page-padding",
|
|
56
|
-
"--resume-page-width"
|
|
54
|
+
"--resume-page-padding"
|
|
57
55
|
]
|
|
58
56
|
},
|
|
59
57
|
{
|
|
@@ -113,8 +111,6 @@
|
|
|
113
111
|
"--resume-font-size-title",
|
|
114
112
|
"--resume-left-col-width",
|
|
115
113
|
"--resume-line-height-body",
|
|
116
|
-
"--resume-page-min-height",
|
|
117
|
-
"--resume-page-width",
|
|
118
114
|
"--resume-space-bullet",
|
|
119
115
|
"--resume-space-item",
|
|
120
116
|
"--resume-space-section"
|
|
@@ -175,9 +171,7 @@
|
|
|
175
171
|
"--resume-font-size-name",
|
|
176
172
|
"--resume-font-size-target",
|
|
177
173
|
"--resume-line-height-body",
|
|
178
|
-
"--resume-page-min-height",
|
|
179
174
|
"--resume-page-padding",
|
|
180
|
-
"--resume-page-width",
|
|
181
175
|
"--resume-space-bullet",
|
|
182
176
|
"--resume-space-header-bottom",
|
|
183
177
|
"--resume-space-item",
|
|
@@ -243,8 +237,6 @@
|
|
|
243
237
|
"--resume-font-size-name",
|
|
244
238
|
"--resume-font-size-target",
|
|
245
239
|
"--resume-line-height-body",
|
|
246
|
-
"--resume-page-min-height",
|
|
247
|
-
"--resume-page-width",
|
|
248
240
|
"--resume-sidebar-width",
|
|
249
241
|
"--resume-space-bullet",
|
|
250
242
|
"--resume-space-item",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Reference Manual 01: Evidence Mining & Anti-Hallucination Gate
|
|
2
|
+
|
|
3
|
+
> **"The goal is not to make the candidate look better than they are. The goal is to make their real strengths visible to the right opportunity."**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. L1~L3 Evidence Classification Standard
|
|
8
|
+
|
|
9
|
+
Every bullet point and technical claim on the resume MUST be grounded in traceable candidate evidence:
|
|
10
|
+
|
|
11
|
+
| Level | Evidence Source | Confidence | Usage & Phrasing Standard | Prohibited Anti-Patterns |
|
|
12
|
+
|:---|:---|:---|:---|:---|
|
|
13
|
+
| **Level 1 (Direct Strong Evidence)** | Code implementation (`src/`), config files (`package.json`, `go.mod`, `Cargo.toml`, `Dockerfile`), API contracts, CI/CD pipelines, Git commit history | 95%+ | Direct, confident assertion with quantified metrics: *"Designed dual-path retrieval pipeline using FastAPI and Qdrant, achieving 92% Top-3 recall rate."* | Fabricating business revenue or claiming ownership beyond repository boundaries. |
|
|
14
|
+
| **Level 2 (Structural Medium Evidence)** | Directory structure, module dependencies, database schemas, framework integrations | 80%~95% | Engineering integration verbs: *"Engineered distributed microservice endpoints for order and inventory management using gRPC."* | Exaggerating simple library imports as custom from-scratch underlying framework development. |
|
|
15
|
+
| **Level 3 (Contextual Weak Inference)** | Agile practices, code reviews, deployment workflows, cross-team collaboration | 60%~80% | Conservative contextual phrasing: *"Participated in system architecture reviews and collaborated with product teams on sprint delivery."* | Claiming solo leadership of large engineering teams without organizational backing. |
|
|
16
|
+
| **Unsupported (No Evidence)** | Facts not found in candidate records or codebases | <50% | **STRICTLY PROHIBITED FROM RESUME** | Inventing unverified degrees, false company tenures, or imaginary revenue numbers. |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Automated Codebase & Git Mining Protocol
|
|
21
|
+
|
|
22
|
+
Run the evidence extraction engine against local repository or workspace:
|
|
23
|
+
```bash
|
|
24
|
+
python3 scripts/evidence/extract-evidence.py --repo . --name "<Candidate Name>" --output workspace/evidence-master.json --quiet
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Extracted Evidence Dimensions:
|
|
28
|
+
1. **Git Provenance**: Author names, email, commit count, active development timeframes;
|
|
29
|
+
2. **Tech Stack Signatures**: Dependency parsing across 25+ language/framework ecosystems;
|
|
30
|
+
3. **Architecture Signals**: Monorepo setups, containerization (Docker/K8s), automated testing, CI/CD workflows;
|
|
31
|
+
4. **Code Volume & Distribution**: Breakdown by language and directory hierarchy.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 3. Anti-Hallucination Execution Checklist
|
|
36
|
+
|
|
37
|
+
Before writing any line into `workspace/resume.html`:
|
|
38
|
+
- [ ] Is the primary technology verified in candidate code, config, or explicit profile?
|
|
39
|
+
- [ ] Is the action verb accurate to actual candidate contribution level (Led vs Implemented vs Participated)?
|
|
40
|
+
- [ ] Are all metrics (percentages, QPS, latency) grounded in reality rather than fabricated?
|
|
41
|
+
- [ ] If JD requires a skill absent from candidate evidence, mark as gap rather than hallucinating.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Reference Manual 02: Career Goal Definition & Role Archetypes
|
|
2
|
+
|
|
3
|
+
> **"Know Yourself. Define Your Direction. Forge Your Opportunity."**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Professional Seniority Signals
|
|
8
|
+
|
|
9
|
+
Identify candidate level and calibrate narrative depth accordingly:
|
|
10
|
+
|
|
11
|
+
| Seniority Tier | Core Value Proposition | Key Narrative Signals | Section Priority |
|
|
12
|
+
|:---|:---|:---|:---|
|
|
13
|
+
| **Junior (1~3 Years)** | Rapid learning, solid coding standards, reliable task delivery | Clean code, unit test coverage, framework fluency, bug resolution | Skills → Projects → Experience → Education |
|
|
14
|
+
| **Mid-Level (3~5 Years)** | Independent ownership, feature architecture, performance optimization | End-to-end module ownership, database tuning, API design, CI/CD | Experience → Projects → Skills → Education |
|
|
15
|
+
| **Senior / Lead (5~8+ Years)** | System architecture, technical leadership, cross-system trade-offs | Distributed systems, high availability, mentoring, business impact | Experience → Projects → Architecture → Skills |
|
|
16
|
+
| **Principal / Architect** | Technical vision, organizational strategy, governance, ROI | Architecture governance, multi-team alignment, technology selection | Experience → Leadership → Architecture → Patents |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Role Archetypes Knowledge Base (`src/knowledge/roles/*.json`)
|
|
21
|
+
|
|
22
|
+
The skill ships with 9+ structured role profiles:
|
|
23
|
+
- `ai-agent-engineer.json`: LLM Orchestration, RAG, Tool Calling, VectorDB, Prompt Engineering
|
|
24
|
+
- `frontend.json`: React/Vue/Next.js, Web Vitals, Design Systems, State Management
|
|
25
|
+
- `java-backend.json`: Spring Boot, JVM tuning, Distributed Caching, Message Queues
|
|
26
|
+
- `node-fullstack.json`: TypeScript, Node.js runtime, GraphQL, Cloud Native
|
|
27
|
+
- `architect.json`: Distributed Systems, Domain-Driven Design, High Concurrency, Cloud Architecture
|
|
28
|
+
- `product-manager.json`: PRD, User Research, Data Analysis, Roadmapping, Growth
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Core Value Proposition Formulation
|
|
33
|
+
|
|
34
|
+
Synthesize the 15~25 word Value Proposition headline following this formula:
|
|
35
|
+
$$\text{Value Proposition} = [\text{Target Role}] + [\text{Years/Domain Focus}] + [\text{Top 2 Signature Strengths}] + [\text{Concrete Impact Deliverable}]$$
|
|
36
|
+
|
|
37
|
+
*Example*:
|
|
38
|
+
> **Senior AI Agent Engineer** with 5+ years building scalable LLM orchestration & enterprise RAG pipelines, delivering 40% latency reduction across multi-agent production workflows.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Reference Manual 03: Job Description (JD) Analysis & Keyword Extraction
|
|
2
|
+
|
|
3
|
+
> **"Understand the hiring manager's unstated pain point to align your verified strengths."**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Automated JD Signal Parsing Protocol
|
|
8
|
+
|
|
9
|
+
Execute the JD analyzer to extract structured requirements:
|
|
10
|
+
```bash
|
|
11
|
+
python3 scripts/evidence/analyze-jd.py --jd "path/to/jd.md" --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Signal Extraction Dimensions:
|
|
15
|
+
1. **Target Role & Seniority Tier**: Extracted from title and years of experience requirements;
|
|
16
|
+
2. **Must-Have Skills (Hard Constraints)**: Top technical stacks and non-negotiable architectural competencies;
|
|
17
|
+
3. **Nice-to-Have Skills (Differentiators)**: Emerging tools, cloud certifications, domain bonuses;
|
|
18
|
+
4. **Hiring Pain Points**: High-frequency verbs indicating immediate business challenges (e.g., "reduce latency", "overhaul legacy monolithic service", "establish CI/CD").
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Keyword Density & Highlighting Rules
|
|
23
|
+
|
|
24
|
+
In `workspace/resume.html`, inject JD keywords to pass ATS automated screening while remaining natural for human reviewers:
|
|
25
|
+
|
|
26
|
+
- **Primary Stack (Top 3~5 Keywords)**: Wrap with `<strong>` tags inside bullet points where evidence exists;
|
|
27
|
+
- **Technology Badges**: Place in `.tech-tags` or `.tech-badge` spans;
|
|
28
|
+
- **Density Control**: Maintain keyword density around 2.5%~4.0% of total words to prevent ATS keyword stuffing penalties.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. FAB (Feature-Advantage-Benefit) Bullet Engineering
|
|
33
|
+
|
|
34
|
+
Structure every project bullet point using the FAB formula:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
38
|
+
│ Feature (技术动作)│ ────> │ Advantage (技术优势)│ ────> │ Benefit (业务价值) │
|
|
39
|
+
│ Exact tech & tool │ │ Architectural gain│ │ Quantified impact│
|
|
40
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
*Before (Weak)*: Worked on database and improved query performance.
|
|
44
|
+
*After (FAB Standard)*: Refactored PostgreSQL indexing and introduced Redis multi-tier caching (**Feature**), cutting P99 query latency from 850ms to 45ms (**Advantage**), supporting 10x traffic spike during peak sales (**Benefit**).
|