knowme-careerforge-skill 0.0.1 → 0.0.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/README.md +6 -9
- package/README.zh-CN.md +6 -9
- package/SKILL.md +95 -116
- package/agents/claude/knowme-careerforge.md +9 -8
- package/agents/codex/knowme-careerforge.yaml +15 -6
- package/agents/cursor/knowme-careerforge.mdc +7 -6
- package/agents/opencode/skill.yaml +1 -1
- package/agents/windsurf/knowme-careerforge.rules +11 -8
- package/dist/cli/src/commands/init.d.ts +1 -1
- package/dist/cli/src/commands/init.js +37 -10
- package/dist/cli/src/index.js +65 -22
- package/dist/scripts/render-pdf.d.ts +0 -9
- package/dist/scripts/render-pdf.js +5 -68
- package/dist/scripts/rendering/render-pdf.d.ts +11 -0
- package/dist/scripts/rendering/render-pdf.js +109 -0
- package/dist/scripts/validate-ats.d.ts +0 -21
- package/dist/scripts/validate-ats.js +5 -132
- package/dist/scripts/validate-layout.d.ts +0 -22
- package/dist/scripts/validate-layout.js +5 -124
- package/dist/scripts/validation/validate-ats.d.ts +23 -0
- package/dist/scripts/validation/validate-ats.js +173 -0
- package/dist/scripts/validation/validate-layout.d.ts +24 -0
- package/dist/scripts/validation/validate-layout.js +164 -0
- package/package.json +15 -12
- package/scripts/Agent.md +138 -0
- package/scripts/{build-gallery.py → build/build-gallery.py} +128 -99
- package/scripts/{build-knowledge.py → build/build-knowledge.py} +9 -1
- package/scripts/{release.py → build/release.py} +20 -16
- package/scripts/{run-all-tests.py → build/run-all-tests.py} +9 -1
- package/scripts/build/sync-version.py +107 -0
- package/scripts/{analyze-jd.py → evidence/analyze-jd.py} +34 -45
- package/scripts/evidence/extract-evidence.py +348 -0
- package/scripts/pipeline/forge.py +142 -0
- package/scripts/rendering/render-pdf.py +162 -0
- package/scripts/{render-pdf.ts → rendering/render-pdf.ts} +3 -3
- package/scripts/template/instantiate-resume.py +178 -0
- package/scripts/{search-template.py → template/search-template.py} +44 -42
- package/scripts/{validate-ats.ts → validation/validate-ats.ts} +39 -35
- package/scripts/{validate-layout.ts → validation/validate-layout.ts} +43 -42
- package/scripts/{validate-resume.py → validation/validate-resume.py} +31 -22
- package/skill.json +1 -1
- package/src/knowledge/index.json +1 -1
- package/src/knowledge/resume-schema.json +112 -0
- package/src/workflows/forge-resume.md +2 -2
- package/src/workflows/review.md +3 -3
- package/scripts/instantiate-resume.py +0 -83
- package/scripts/sync-version.py +0 -57
package/scripts/Agent.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# KnowMe CareerForge — Scripts Architecture & Agent Execution Specification
|
|
2
|
+
|
|
3
|
+
> **定位**:面向 AI Agent 与研发工程师的 `scripts/` 工具链架构规范、模块划分标准与执行契约手册。
|
|
4
|
+
>
|
|
5
|
+
> **版本**:v1.0.0-modular
|
|
6
|
+
> **核心原则**:Domain-Driven Modularization · Quiet Agent Execution · Deterministic Rendering · Zero-Dependency Python Core
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. 目录架构与模块职责划分 (Domain Architecture)
|
|
11
|
+
|
|
12
|
+
为了保证脚本工具链的高内聚、低耦合与易维护性,`scripts/` 目录划分为 **6 大功能领域模块**:
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
scripts/
|
|
16
|
+
├── Agent.md # 本规范文档 (Source of Truth for Agent/Dev)
|
|
17
|
+
│
|
|
18
|
+
├── pipeline/ # 【模块 1: 端到端统筹管线】
|
|
19
|
+
│ └── forge.py # 一键端到端装配引擎 (One-Shot Pipeline: 挖掘 -> 装配 -> QA -> PDF)
|
|
20
|
+
│
|
|
21
|
+
├── evidence/ # 【模块 2: 事实与证据挖掘 (repo-to-resume)】
|
|
22
|
+
│ ├── extract-evidence.py # 代码仓与 Git 事实挖掘器 (提取技术栈、架构信号、Git 贡献 -> L1~L3 证据链)
|
|
23
|
+
│ └── analyze-jd.py # 目标 JD 深度分析器 (提取职级、分类、必备/加分项、痛点关键词)
|
|
24
|
+
│
|
|
25
|
+
├── template/ # 【模块 3: 模板检索与工作区装配】
|
|
26
|
+
│ ├── search-template.py # 多维加权打分搜索引擎 (BM25 + 岗位/风格/页数/密度推荐)
|
|
27
|
+
│ └── instantiate-resume.py # 模板装配与数据注入引擎 (CSS 内联单文件 + 结构化数据注入 + 关键词高亮)
|
|
28
|
+
│
|
|
29
|
+
├── validation/ # 【模块 4: 双重 QA 自动化质检】
|
|
30
|
+
│ ├── validate-resume.py # 语义结构、Design Tokens 与字符密度轻量校验器
|
|
31
|
+
│ ├── validate-layout.ts # Playwright DOM 盒模型物理高度 (1122.5px A4) 溢出与孤行检测
|
|
32
|
+
│ └── validate-ats.ts # ATS 纯文本流解析、联系方式与 H1/H2 标头层级校验
|
|
33
|
+
│
|
|
34
|
+
├── rendering/ # 【模块 5: 确定性 PDF 渲染导出】
|
|
35
|
+
│ ├── render-pdf.py # 跨平台自愈式 PDF 渲染调度器 (自动探测 Playwright 及全平台 Chromium/Edge/Brave)
|
|
36
|
+
│ └── render-pdf.ts # Playwright 驱动的无损矢量 A4 PDF 渲染器
|
|
37
|
+
│
|
|
38
|
+
└── build/ # 【模块 6: 编译构建、测试与发布工具链】
|
|
39
|
+
├── build-knowledge.py # 岗位画像与模板知识库索引编译器 (生成 index.json & templates.json)
|
|
40
|
+
├── build-gallery.py # 静态交互式模板画廊生成器 (编译 output/templates_gallery/)
|
|
41
|
+
├── run-all-tests.py # 全链路自动化测试运行器 (discover & run tests/)
|
|
42
|
+
├── sync-version.py # 多清单版本号同步器 (package.json / pyproject.toml / skill.json / cli)
|
|
43
|
+
└── release.py # 自动化发布流水线引擎 (版本对齐 -> 编译 -> 测试 -> 打包预检)
|
|
44
|
+
```
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. 脚本契约与 Agent 调用规范 (Execution Contracts)
|
|
48
|
+
|
|
49
|
+
### 2.1 推荐调用模式:一键直达管线 (One-Shot Execution)
|
|
50
|
+
|
|
51
|
+
AI Agent 响应用户求职定制需求时,**强烈推荐首选一键管线**:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 静默全流程执行 (后台自动完成:代码仓扫描 -> 模板检索 -> 工作区注入 -> Dual QA -> PDF 导出)
|
|
55
|
+
python3 scripts/pipeline/forge.py --repo . --role "<Target Role>" --jd "<path/to/jd.txt>" --template modern --quiet
|
|
56
|
+
```
|
|
57
|
+
* **CLI 等价形式**:`knowme forge --repo . --role "<Target Role>" --template modern --quiet`
|
|
58
|
+
* **返回格式**:结构化 JSON,供 Agent 直接获取交付文件路径:
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"status": "SUCCESS",
|
|
62
|
+
"htmlCanvas": "workspace/resume.html",
|
|
63
|
+
"pdfDelivery": "workspace/resume.pdf",
|
|
64
|
+
"evidenceProfile": "workspace/evidence-master.json",
|
|
65
|
+
"templateUsed": "modern"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 2.2 细粒度分步调用接口 (Granular Step-by-Step APIs)
|
|
72
|
+
|
|
73
|
+
当 Agent 或用户需要分步微调、审查证据或调校 Design Tokens 时,按以下顺序执行:
|
|
74
|
+
|
|
75
|
+
| 步骤 | 阶段 | 推荐命令 | 关键参数 | 产出物 |
|
|
76
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
77
|
+
| **Step 1** | **事实挖掘** | `python3 scripts/evidence/extract-evidence.py` | `--repo <dir>` `--name <name>` `--quiet` | `workspace/evidence-master.json` |
|
|
78
|
+
| **Step 2** | **JD 解析** | `python3 scripts/evidence/analyze-jd.py` | `--jd <file>` 或 `--text "<str>"` `--json` | 提取必须技能与权重关键词 |
|
|
79
|
+
| **Step 3** | **模板检索** | `python3 scripts/template/search-template.py` | `"<Role>"` `--style "<style>"` `--json` | 加权推荐模板列表 (Top 1) |
|
|
80
|
+
| **Step 4** | **画布装配** | `python3 scripts/template/instantiate-resume.py` | `--template <id>` `--profile <json>` `--keywords <kw>` | `workspace/resume.html` (单文件内联) |
|
|
81
|
+
| **Step 5** | **质检自愈** | `python3 scripts/validation/validate-resume.py` | `--html workspace/resume.html` `--expected-pages 1` | QA 诊断状态与溢出提示 |
|
|
82
|
+
| **Step 6** | **PDF 导出** | `python3 scripts/rendering/render-pdf.py` | `workspace/resume.html` `workspace/resume.pdf` `--quiet` | `workspace/resume.pdf` (确定性矢量) |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Agent 对话降噪规范 (Strict Quiet Execution Protocol)
|
|
87
|
+
|
|
88
|
+
为了杜绝对话框中出现几百行冗余代码或中间脚本,所有接入的 AI Agent 必须遵守以下铁律:
|
|
89
|
+
|
|
90
|
+
1. **禁止在 Chat 中打印原始 HTML/CSS**:禁止将 `workspace/resume.html` 的数百行代码打印到对话框中;
|
|
91
|
+
2. **禁止在 Chat 中编写现场临时脚本**:严禁现场生成临时验证脚本或修改场脚本,必须使用 `scripts/` 下既有的标准工具链;
|
|
92
|
+
3. **静默执行原则**:调用脚本时统一添加 `--quiet` 或 `--json` 参数;
|
|
93
|
+
4. **标准交付物格式**:
|
|
94
|
+
```markdown
|
|
95
|
+
### 🎯 职业定位与核心价值主张 (Core Value Proposition)
|
|
96
|
+
> **[姓名] · [目标岗位]**
|
|
97
|
+
> *[15~25字核心定位与价值阐述]*
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### 🛡️ Top 3 真实证据链亮点 (L1/L2 级别可追溯事实)
|
|
102
|
+
1. **[业务方向/核心架构]**: [动词 + 可量化成果 + 证据来源]
|
|
103
|
+
2. **[核心技术栈工程化]**: [关键框架与工程规范落地]
|
|
104
|
+
3. **[交付与系统效能]**: [稳定性/CI-CD/性能指标]
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### 📄 最终交付产物
|
|
109
|
+
- **PDF 终稿 (确定性 A4)**: `workspace/resume.pdf`
|
|
110
|
+
- **HTML 修改场 (Design Tokens)**: `workspace/resume.html`
|
|
111
|
+
- **证据事实底座**: `workspace/evidence-master.json`
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 4. 后续开发与扩展规范 (Developer Standards)
|
|
117
|
+
|
|
118
|
+
为保持工程整洁与跨平台稳定性,后续新增或修改脚本必须遵循以下标准:
|
|
119
|
+
|
|
120
|
+
### 4.1 根路径自适应解析 (Root Path Resolution Standard)
|
|
121
|
+
所有 Python 脚本**严禁使用硬编码相对路径**,必须通过自适应探测函数定位项目根目录:
|
|
122
|
+
```python
|
|
123
|
+
def get_project_root() -> Path:
|
|
124
|
+
curr = Path(__file__).resolve().parent
|
|
125
|
+
for _ in range(5):
|
|
126
|
+
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
127
|
+
return curr
|
|
128
|
+
curr = curr.parent
|
|
129
|
+
return Path.cwd()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 4.2 零重型依赖原则 (Zero-Heavy-Dependency)
|
|
133
|
+
* 核心运行脚本(`evidence/`、`template/`、`pipeline/`)**仅使用 Python 3.9+ 标准库**(`json`, `argparse`, `pathlib`, `re`, `subprocess` 等);
|
|
134
|
+
* 避免引入大型三方包(如 pandas、scikit-learn),保证轻量秒开与跨平台零安装成本。
|
|
135
|
+
|
|
136
|
+
### 4.3 渲染与 QA 规范
|
|
137
|
+
* A4 绝对高度基准固化为 `1122.5px` ($297\text{mm} \times 96\text{DPI} / 25.4$);
|
|
138
|
+
* 任何涉及排版验证与 PDF 导出的改动,必须通过 `scripts/build/run-all-tests.py` 全量测试套件验证。
|
|
@@ -7,8 +7,16 @@ KnowMe CareerForge — Template Gallery Builder
|
|
|
7
7
|
import os, sys, json, re
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
def get_project_root() -> Path:
|
|
11
|
+
curr = Path(__file__).resolve().parent
|
|
12
|
+
for _ in range(5):
|
|
13
|
+
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
14
|
+
return curr
|
|
15
|
+
curr = curr.parent
|
|
16
|
+
return Path.cwd()
|
|
17
|
+
|
|
10
18
|
def build_gallery():
|
|
11
|
-
base_dir =
|
|
19
|
+
base_dir = get_project_root()
|
|
12
20
|
templates_dir = base_dir / "src" / "templates"
|
|
13
21
|
gallery_dir = base_dir / "output" / "templates_gallery"
|
|
14
22
|
gallery_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -33,28 +41,30 @@ def build_gallery():
|
|
|
33
41
|
|
|
34
42
|
try:
|
|
35
43
|
meta = json.loads(meta_file.read_text(encoding="utf-8"))
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
html_content = html_file.read_text(encoding="utf-8")
|
|
45
|
+
css_content = css_file.read_text(encoding="utf-8")
|
|
38
46
|
|
|
39
|
-
#
|
|
40
|
-
inlined_preview =
|
|
47
|
+
# 编译内联单文件独立预览页面
|
|
48
|
+
inlined_preview = html_content.replace(
|
|
41
49
|
'<link rel="stylesheet" href="style.css">',
|
|
42
|
-
f'<style>\n{
|
|
50
|
+
f'<style>\n{css_content}\n </style>'
|
|
43
51
|
)
|
|
44
52
|
|
|
45
|
-
preview_path = gallery_dir / f"{
|
|
53
|
+
preview_path = gallery_dir / f"{t_dir.name}.html"
|
|
46
54
|
preview_path.write_text(inlined_preview, encoding="utf-8")
|
|
47
55
|
|
|
48
56
|
gallery_items.append({
|
|
49
|
-
"id": meta
|
|
50
|
-
"name": meta.get("name",
|
|
51
|
-
"style": meta.get("style", "
|
|
57
|
+
"id": meta.get("id", t_dir.name),
|
|
58
|
+
"name": meta.get("name", t_dir.name),
|
|
59
|
+
"style": meta.get("style", "single-column"),
|
|
52
60
|
"category": meta.get("roleCategory", "engineering-ai"),
|
|
61
|
+
"tone": meta.get("visualStyle", {}).get("tone", "modern"),
|
|
53
62
|
"accent": meta.get("visualStyle", {}).get("accentColor", "#2563eb"),
|
|
54
|
-
"
|
|
63
|
+
"pages": meta.get("layout", {}).get("targetPages", 1),
|
|
55
64
|
"density": meta.get("layout", {}).get("density", "balanced"),
|
|
56
|
-
"
|
|
57
|
-
"
|
|
65
|
+
"atsTier": meta.get("atsScoreTier", "tier-1-optimal"),
|
|
66
|
+
"previewFile": f"{t_dir.name}.html",
|
|
67
|
+
"supported": meta.get("supportedRoles", [])
|
|
58
68
|
})
|
|
59
69
|
print(f"[✓] Generated Gallery Preview -> {preview_path.name}")
|
|
60
70
|
except Exception as e:
|
|
@@ -66,132 +76,149 @@ def build_gallery():
|
|
|
66
76
|
<head>
|
|
67
77
|
<meta charset="UTF-8">
|
|
68
78
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
69
|
-
<title>KnowMe CareerForge —
|
|
79
|
+
<title>KnowMe CareerForge — HTML Resume Template Gallery</title>
|
|
70
80
|
<style>
|
|
71
81
|
:root {{
|
|
72
|
-
--
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
--
|
|
76
|
-
--
|
|
77
|
-
--
|
|
82
|
+
--primary: #0f172a;
|
|
83
|
+
--secondary: #475569;
|
|
84
|
+
--accent: #2563eb;
|
|
85
|
+
--bg: #f8fafc;
|
|
86
|
+
--card-bg: #ffffff;
|
|
87
|
+
--border: #e2e8f0;
|
|
78
88
|
}}
|
|
79
89
|
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
|
|
80
90
|
body {{
|
|
81
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", sans-serif;
|
|
82
|
-
background: var(--bg);
|
|
83
|
-
color: var(--
|
|
84
|
-
padding:
|
|
91
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
92
|
+
background-color: var(--bg);
|
|
93
|
+
color: var(--primary);
|
|
94
|
+
padding: 40px 20px;
|
|
95
|
+
line-height: 1.5;
|
|
85
96
|
}}
|
|
86
|
-
.container {{
|
|
87
|
-
max-width:
|
|
97
|
+
.gallery-container {{
|
|
98
|
+
max-width: 1200px;
|
|
88
99
|
margin: 0 auto;
|
|
89
100
|
}}
|
|
90
|
-
|
|
101
|
+
header {{
|
|
91
102
|
text-align: center;
|
|
92
|
-
margin-bottom:
|
|
103
|
+
margin-bottom: 40px;
|
|
104
|
+
}}
|
|
105
|
+
h1 {{
|
|
106
|
+
font-size: 28px;
|
|
107
|
+
font-weight: 800;
|
|
108
|
+
color: var(--primary);
|
|
109
|
+
margin-bottom: 8px;
|
|
110
|
+
}}
|
|
111
|
+
.subtitle {{
|
|
112
|
+
color: var(--secondary);
|
|
113
|
+
font-size: 15px;
|
|
93
114
|
}}
|
|
94
|
-
h1 {{ font-size: 26px; color: var(--accent); margin-bottom: 8px; font-weight: 800; }}
|
|
95
|
-
p.subtitle {{ color: var(--muted); font-size: 14px; max-width: 680px; margin: 0 auto; line-height: 1.5; }}
|
|
96
115
|
.grid {{
|
|
97
116
|
display: grid;
|
|
98
|
-
grid-template-columns: repeat(auto-fit, minmax(
|
|
117
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
99
118
|
gap: 24px;
|
|
100
119
|
}}
|
|
101
120
|
.card {{
|
|
102
121
|
background: var(--card-bg);
|
|
103
122
|
border: 1px solid var(--border);
|
|
104
123
|
border-radius: 12px;
|
|
105
|
-
|
|
124
|
+
overflow: hidden;
|
|
125
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
|
126
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
106
127
|
display: flex;
|
|
107
128
|
flex-direction: column;
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
}}
|
|
130
|
+
.card:hover {{
|
|
131
|
+
transform: translateY(-4px);
|
|
132
|
+
box-shadow: 0 12px 20px -3px rgba(0, 0, 0, 0.1);
|
|
110
133
|
}}
|
|
111
134
|
.card-header {{
|
|
135
|
+
padding: 16px 20px;
|
|
136
|
+
border-bottom: 1px solid var(--border);
|
|
137
|
+
background: #f1f5f9;
|
|
112
138
|
display: flex;
|
|
113
139
|
justify-content: space-between;
|
|
114
|
-
align-items:
|
|
115
|
-
border-bottom: 1px solid var(--border);
|
|
116
|
-
padding-bottom: 12px;
|
|
140
|
+
align-items: center;
|
|
117
141
|
}}
|
|
118
142
|
.card-title {{
|
|
119
|
-
font-size:
|
|
143
|
+
font-size: 16px;
|
|
120
144
|
font-weight: 700;
|
|
121
|
-
color: var(--text);
|
|
122
145
|
}}
|
|
123
146
|
.badge {{
|
|
124
147
|
font-size: 11px;
|
|
125
|
-
padding:
|
|
126
|
-
border-radius:
|
|
127
|
-
background:
|
|
128
|
-
color: var(--
|
|
129
|
-
|
|
148
|
+
padding: 2px 8px;
|
|
149
|
+
border-radius: 9999px;
|
|
150
|
+
background: #e2e8f0;
|
|
151
|
+
color: var(--secondary);
|
|
152
|
+
font-weight: 600;
|
|
130
153
|
}}
|
|
131
|
-
.
|
|
154
|
+
.badge.optimal {{
|
|
155
|
+
background: #dcfce7;
|
|
156
|
+
color: #166534;
|
|
157
|
+
}}
|
|
158
|
+
.preview-frame-wrapper {{
|
|
159
|
+
height: 380px;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
position: relative;
|
|
162
|
+
background: #e2e8f0;
|
|
163
|
+
}}
|
|
164
|
+
.preview-frame {{
|
|
165
|
+
width: 210mm;
|
|
166
|
+
height: 297mm;
|
|
167
|
+
transform: scale(0.35);
|
|
168
|
+
transform-origin: top left;
|
|
169
|
+
border: none;
|
|
170
|
+
pointer-events: none;
|
|
171
|
+
background: #fff;
|
|
172
|
+
}}
|
|
173
|
+
.card-body {{
|
|
174
|
+
padding: 16px 20px;
|
|
175
|
+
flex: 1;
|
|
132
176
|
display: flex;
|
|
133
177
|
flex-direction: column;
|
|
134
|
-
|
|
135
|
-
font-size: 13px;
|
|
136
|
-
color: var(--muted);
|
|
178
|
+
justify-content: space-between;
|
|
137
179
|
}}
|
|
138
|
-
.meta-
|
|
139
|
-
|
|
180
|
+
.meta-list {{
|
|
181
|
+
font-size: 13px;
|
|
182
|
+
color: var(--secondary);
|
|
183
|
+
margin-bottom: 12px;
|
|
184
|
+
line-height: 1.6;
|
|
140
185
|
}}
|
|
141
|
-
.
|
|
186
|
+
.tag-container {{
|
|
142
187
|
display: flex;
|
|
143
188
|
flex-wrap: wrap;
|
|
144
|
-
gap:
|
|
145
|
-
margin-
|
|
189
|
+
gap: 4px;
|
|
190
|
+
margin-bottom: 16px;
|
|
146
191
|
}}
|
|
147
192
|
.tag {{
|
|
148
|
-
background: #334155;
|
|
149
193
|
font-size: 11px;
|
|
194
|
+
background: #f1f5f9;
|
|
195
|
+
color: #334155;
|
|
150
196
|
padding: 2px 6px;
|
|
151
197
|
border-radius: 4px;
|
|
152
|
-
color: #e2e8f0;
|
|
153
|
-
}}
|
|
154
|
-
.frame-wrapper {{
|
|
155
|
-
width: 100%;
|
|
156
|
-
height: 480px;
|
|
157
|
-
border: 1px solid var(--border);
|
|
158
|
-
border-radius: 8px;
|
|
159
|
-
overflow: hidden;
|
|
160
|
-
background: #ffffff;
|
|
161
|
-
position: relative;
|
|
162
|
-
}}
|
|
163
|
-
iframe {{
|
|
164
|
-
width: 210mm;
|
|
165
|
-
height: 297mm;
|
|
166
|
-
border: none;
|
|
167
|
-
transform: scale(0.48);
|
|
168
|
-
transform-origin: top left;
|
|
169
|
-
position: absolute;
|
|
170
|
-
top: 0;
|
|
171
|
-
left: 0;
|
|
172
|
-
pointer-events: none;
|
|
173
198
|
}}
|
|
174
199
|
.btn {{
|
|
175
|
-
display:
|
|
200
|
+
display: block;
|
|
176
201
|
text-align: center;
|
|
202
|
+
padding: 8px 16px;
|
|
177
203
|
background: var(--accent);
|
|
178
|
-
color: #
|
|
179
|
-
|
|
204
|
+
color: #fff;
|
|
205
|
+
text-decoration: none;
|
|
180
206
|
border-radius: 6px;
|
|
207
|
+
font-weight: 600;
|
|
181
208
|
font-size: 13px;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
209
|
+
transition: background 0.2s;
|
|
210
|
+
}}
|
|
211
|
+
.btn:hover {{
|
|
212
|
+
background: #1d4ed8;
|
|
185
213
|
}}
|
|
186
|
-
.btn:hover {{ opacity: 0.9; }}
|
|
187
214
|
</style>
|
|
188
215
|
</head>
|
|
189
216
|
<body>
|
|
190
|
-
<div class="container">
|
|
191
|
-
<
|
|
192
|
-
<h1>KnowMe CareerForge —
|
|
193
|
-
<p class="subtitle"
|
|
194
|
-
</
|
|
217
|
+
<div class="gallery-container">
|
|
218
|
+
<header>
|
|
219
|
+
<h1>KnowMe CareerForge — Template Gallery</h1>
|
|
220
|
+
<p class="subtitle">A4 Deterministic HTML Resume Templates with Design Tokens Calibration</p>
|
|
221
|
+
</header>
|
|
195
222
|
<div class="grid">
|
|
196
223
|
"""
|
|
197
224
|
|
|
@@ -200,21 +227,23 @@ def build_gallery():
|
|
|
200
227
|
gallery_html += f"""
|
|
201
228
|
<div class="card">
|
|
202
229
|
<div class="card-header">
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
<div style="font-size: 12px; color: var(--muted); margin-top: 2px;">ID: <code>{item['id']}</code></div>
|
|
206
|
-
</div>
|
|
207
|
-
<span class="badge">{item['style']}</span>
|
|
230
|
+
<span class="card-title">{item['name']}</span>
|
|
231
|
+
<span class="badge optimal">ATS Tier 1</span>
|
|
208
232
|
</div>
|
|
209
|
-
<div class="
|
|
210
|
-
<
|
|
211
|
-
<div class="meta-item"><strong>支持岗位:</strong></div>
|
|
212
|
-
<div class="tags">{tags_html}</div>
|
|
233
|
+
<div class="preview-frame-wrapper">
|
|
234
|
+
<iframe class="preview-frame" src="{item['previewFile']}"></iframe>
|
|
213
235
|
</div>
|
|
214
|
-
<div class="
|
|
215
|
-
<
|
|
236
|
+
<div class="card-body">
|
|
237
|
+
<div class="meta-list">
|
|
238
|
+
<div><strong>Style:</strong> {item['style']}</div>
|
|
239
|
+
<div><strong>Target:</strong> {item['pages']} page(s) ({item['density']} density)</div>
|
|
240
|
+
<div><strong>Tone:</strong> {item['tone']}</div>
|
|
241
|
+
</div>
|
|
242
|
+
<div class="tag-container">
|
|
243
|
+
{tags_html}
|
|
244
|
+
</div>
|
|
245
|
+
<a class="btn" href="{item['previewFile']}" target="_blank">Open Full A4 Preview ↗</a>
|
|
216
246
|
</div>
|
|
217
|
-
<a href="{item['previewUrl']}" target="_blank" class="btn">打开独立预览 (A4 全尺寸)</a>
|
|
218
247
|
</div>
|
|
219
248
|
"""
|
|
220
249
|
|
|
@@ -7,8 +7,16 @@ KnowMe CareerForge — Knowledge & Template Index Compiler
|
|
|
7
7
|
import sys, os, json, re
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
def get_project_root() -> Path:
|
|
11
|
+
curr = Path(__file__).resolve().parent
|
|
12
|
+
for _ in range(5):
|
|
13
|
+
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
14
|
+
return curr
|
|
15
|
+
curr = curr.parent
|
|
16
|
+
return Path.cwd()
|
|
17
|
+
|
|
10
18
|
def build_knowledge_index():
|
|
11
|
-
base_dir =
|
|
19
|
+
base_dir = get_project_root()
|
|
12
20
|
knowledge_dir = base_dir / "src" / "knowledge"
|
|
13
21
|
templates_dir = base_dir / "src" / "templates"
|
|
14
22
|
|
|
@@ -7,6 +7,14 @@ KnowMe CareerForge — Release & NPM Publishing Automation Script
|
|
|
7
7
|
import sys, os, subprocess, json, re, argparse
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
def get_project_root() -> Path:
|
|
11
|
+
curr = Path(__file__).resolve().parent
|
|
12
|
+
for _ in range(5):
|
|
13
|
+
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
14
|
+
return curr
|
|
15
|
+
curr = curr.parent
|
|
16
|
+
return Path.cwd()
|
|
17
|
+
|
|
10
18
|
def run_command(cmd, cwd, desc):
|
|
11
19
|
print(f"\n[+] {desc}...")
|
|
12
20
|
proc = subprocess.run(cmd, shell=True, text=True, cwd=cwd, capture_output=True)
|
|
@@ -24,7 +32,7 @@ def main():
|
|
|
24
32
|
parser.add_argument("--publish", action="store_true", help="Directly trigger npm publish --access public after verification")
|
|
25
33
|
|
|
26
34
|
args = parser.parse_args()
|
|
27
|
-
base_dir =
|
|
35
|
+
base_dir = get_project_root()
|
|
28
36
|
|
|
29
37
|
# 1. 确定版本号
|
|
30
38
|
pkg_file = base_dir / "package.json"
|
|
@@ -47,23 +55,23 @@ def main():
|
|
|
47
55
|
|
|
48
56
|
# 2. 同步多文件版本号
|
|
49
57
|
if target_version != current_version or args.version:
|
|
50
|
-
sync_script = base_dir / "scripts" / "sync-version.py"
|
|
58
|
+
sync_script = base_dir / "scripts" / "build" / "sync-version.py"
|
|
51
59
|
run_command(f"python3 {sync_script} {target_version}", base_dir, f"Synchronizing version to v{target_version}")
|
|
52
60
|
|
|
53
61
|
# 3. 重新编译知识库索引
|
|
54
|
-
build_kb = base_dir / "scripts" / "build-knowledge.py"
|
|
62
|
+
build_kb = base_dir / "scripts" / "build" / "build-knowledge.py"
|
|
55
63
|
run_command(f"python3 {build_kb}", base_dir, "Compiling Knowledge Base Index")
|
|
56
64
|
|
|
57
65
|
# 4. 重新构建模板画廊
|
|
58
|
-
build_gal = base_dir / "scripts" / "build-gallery.py"
|
|
66
|
+
build_gal = base_dir / "scripts" / "build" / "build-gallery.py"
|
|
59
67
|
run_command(f"python3 {build_gal}", base_dir, "Building HTML Template Gallery")
|
|
60
68
|
|
|
61
69
|
# 5. 编译 TypeScript CLI 产物
|
|
62
70
|
run_command("npm run build", base_dir, "Compiling TypeScript CLI (tsc -> dist/)")
|
|
63
71
|
|
|
64
72
|
# 6. 运行全链路自动化测试套件
|
|
65
|
-
test_script = base_dir / "scripts" / "run-all-tests.py"
|
|
66
|
-
run_command(f"python3 {test_script}", base_dir, "Running Full-Chain Test Suites
|
|
73
|
+
test_script = base_dir / "scripts" / "build" / "run-all-tests.py"
|
|
74
|
+
run_command(f"python3 {test_script}", base_dir, "Running Full-Chain Test Suites")
|
|
67
75
|
|
|
68
76
|
# 7. NPM 打包预检 (npm pack --dry-run)
|
|
69
77
|
pack_output = run_command("npm pack --dry-run --json", base_dir, "Inspecting NPM Package Files")
|
|
@@ -71,8 +79,8 @@ def main():
|
|
|
71
79
|
try:
|
|
72
80
|
pack_data = json.loads(pack_output)
|
|
73
81
|
if isinstance(pack_data, list) and len(pack_data) > 0:
|
|
74
|
-
files_count = pack_data[0].get("entryCount",
|
|
75
|
-
unpacked_size = pack_data[0].get("unpackedSize",
|
|
82
|
+
files_count = pack_data[0].get("entryCount", 0)
|
|
83
|
+
unpacked_size = pack_data[0].get("unpackedSize", 0)
|
|
76
84
|
print(f"\n[📦 NPM Package Stats]: {files_count} files included, Unpacked Size: ~{round(unpacked_size / 1024, 1)} KB")
|
|
77
85
|
except:
|
|
78
86
|
pass
|
|
@@ -80,7 +88,7 @@ def main():
|
|
|
80
88
|
# 8. 发布执行或指引
|
|
81
89
|
print("\n" + "=" * 62)
|
|
82
90
|
print(f" [✓] Release Preparation for v{target_version} Completed!")
|
|
83
|
-
print("
|
|
91
|
+
print("==============================================================")
|
|
84
92
|
|
|
85
93
|
if args.publish:
|
|
86
94
|
print("\n[🚀 Triggering NPM Publish...]")
|
|
@@ -88,13 +96,9 @@ def main():
|
|
|
88
96
|
print(f"\n🎉 Successfully published knowme-careerforge-skill@v{target_version} to NPM!")
|
|
89
97
|
else:
|
|
90
98
|
print("\nNext steps to publish your package to NPM:")
|
|
91
|
-
print(" 1. Review
|
|
92
|
-
print(f"
|
|
93
|
-
print(f"
|
|
94
|
-
print("\n 2. Publish to NPM registry:")
|
|
95
|
-
print(" npm publish --access public")
|
|
96
|
-
print("\n 3. Verify on NPM:")
|
|
97
|
-
print(f" npm view knowme-careerforge-skill@v{target_version}")
|
|
99
|
+
print(f" 1. Review packaged assets and git status.")
|
|
100
|
+
print(f" 2. Run: npm publish --access public\n")
|
|
101
|
+
print(f" 3. Verify with:")
|
|
98
102
|
print(f" npx knowme-careerforge-skill@{target_version} list\n")
|
|
99
103
|
|
|
100
104
|
if __name__ == "__main__":
|
|
@@ -7,9 +7,17 @@ KnowMe CareerForge — Unified Full-Chain Test Runner
|
|
|
7
7
|
import os, sys, unittest, time
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
def get_project_root() -> Path:
|
|
11
|
+
curr = Path(__file__).resolve().parent
|
|
12
|
+
for _ in range(5):
|
|
13
|
+
if (curr / "SKILL.md").exists() or (curr / "package.json").exists():
|
|
14
|
+
return curr
|
|
15
|
+
curr = curr.parent
|
|
16
|
+
return Path.cwd()
|
|
17
|
+
|
|
10
18
|
def run_all_test_suites():
|
|
11
19
|
start_time = time.time()
|
|
12
|
-
base_dir =
|
|
20
|
+
base_dir = get_project_root()
|
|
13
21
|
tests_dir = base_dir / "tests"
|
|
14
22
|
|
|
15
23
|
print("==============================================================")
|