knowme-careerforge-skill 0.0.4 → 0.0.7

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.
Files changed (117) hide show
  1. package/AGENT.md +276 -0
  2. package/ARCHITECTURE.md +220 -0
  3. package/README.md +46 -63
  4. package/README.zh-CN.md +47 -66
  5. package/SKILL.md +38 -135
  6. package/agents/claude/knowme-careerforge.md +2 -17
  7. package/agents/codex/knowme-careerforge.yaml +2 -20
  8. package/agents/cursor/knowme-careerforge.mdc +4 -13
  9. package/agents/gemini/knowme-careerforge.json +1 -1
  10. package/agents/opencode/skill.yaml +1 -1
  11. package/agents/windsurf/knowme-careerforge.rules +2 -12
  12. package/dist/cli/src/commands/init.js +51 -32
  13. package/dist/cli/src/index.js +25 -21
  14. package/dist/scripts/rendering/engine-loader.d.ts +2 -0
  15. package/dist/scripts/rendering/engine-loader.js +46 -0
  16. package/dist/scripts/rendering/render-pdf.d.ts +3 -8
  17. package/dist/scripts/rendering/render-pdf.js +7 -103
  18. package/dist/scripts/rendering/runtime.d.ts +9 -0
  19. package/dist/scripts/rendering/runtime.js +63 -0
  20. package/dist/scripts/validation/validate-ats.d.ts +2 -22
  21. package/dist/scripts/validation/validate-ats.js +6 -168
  22. package/dist/scripts/validation/validate-layout.d.ts +2 -23
  23. package/dist/scripts/validation/validate-layout.js +5 -159
  24. package/package.json +15 -4
  25. package/references/01-evidence-mining.md +35 -0
  26. package/references/02-career-goal.md +40 -0
  27. package/references/03-jd-analysis.md +32 -0
  28. package/references/04-template-selection.md +30 -0
  29. package/references/05-html-canvas-tokens.md +12 -0
  30. package/references/06-qa-and-rendering.md +27 -0
  31. package/references/07-artifact-contract.md +83 -0
  32. package/scripts/Agent.md +9 -136
  33. package/scripts/build/build-gallery.py +66 -56
  34. package/scripts/build/run-all-tests.py +1 -0
  35. package/scripts/build/sync-version.py +27 -9
  36. package/scripts/contracts/profile.py +101 -0
  37. package/scripts/evidence/extract-evidence.py +109 -62
  38. package/scripts/pipeline/forge.py +276 -136
  39. package/scripts/rendering/README.md +65 -0
  40. package/scripts/rendering/browser-engine.d.ts +15 -0
  41. package/scripts/rendering/browser-engine.js +408 -0
  42. package/scripts/rendering/browser_engine.py +39 -0
  43. package/scripts/rendering/engine-loader.ts +11 -0
  44. package/scripts/rendering/render-pdf.py +26 -151
  45. package/scripts/rendering/render-pdf.ts +7 -83
  46. package/scripts/template/instantiate-resume.py +266 -173
  47. package/scripts/template/search-template.py +89 -3
  48. package/scripts/validation/measure-dom.js +5 -0
  49. package/scripts/validation/validate-ats.ts +6 -174
  50. package/scripts/validation/validate-layout.ts +5 -159
  51. package/scripts/validation/validate-resume.py +23 -134
  52. package/skill.json +7 -6
  53. package/src/knowledge/index.json +391 -3
  54. package/src/knowledge/resume-schema.json +377 -71
  55. package/src/knowledge/templates.json +389 -1
  56. package/src/references/01-evidence-mining.md +24 -30
  57. package/src/references/02-career-goal.md +4 -2
  58. package/src/references/03-jd-analysis.md +5 -17
  59. package/src/references/04-template-selection.md +20 -33
  60. package/src/references/05-html-canvas-tokens.md +6 -45
  61. package/src/references/06-qa-and-rendering.md +16 -43
  62. package/src/references/07-artifact-contract.md +83 -0
  63. package/src/templates/Agent.md +11 -0
  64. package/src/templates/academic-research/README.md +25 -0
  65. package/src/templates/academic-research/canvas.html +21 -0
  66. package/src/templates/academic-research/metadata.json +38 -0
  67. package/src/templates/academic-research/sample-profile.json +86 -0
  68. package/src/templates/academic-research/style.css +213 -0
  69. package/src/templates/classic/README.md +9 -0
  70. package/src/templates/classic/canvas.html +21 -0
  71. package/src/templates/classic/metadata.json +3 -0
  72. package/src/templates/classic/sample-profile.json +86 -0
  73. package/src/templates/classic/style.css +14 -4
  74. package/src/templates/common/base.css +32 -0
  75. package/src/templates/common/canvas-bindings.css +41 -0
  76. package/src/templates/common/resume-contract.md +8 -0
  77. package/src/templates/compact-dense/README.md +25 -0
  78. package/src/templates/compact-dense/canvas.html +14 -0
  79. package/src/templates/compact-dense/metadata.json +38 -0
  80. package/src/templates/compact-dense/sample-profile.json +86 -0
  81. package/src/templates/compact-dense/style.css +218 -0
  82. package/src/templates/creative-tech/README.md +26 -0
  83. package/src/templates/creative-tech/canvas.html +15 -0
  84. package/src/templates/creative-tech/metadata.json +39 -0
  85. package/src/templates/creative-tech/sample-profile.json +86 -0
  86. package/src/templates/creative-tech/style.css +257 -0
  87. package/src/templates/data-analyst/README.md +26 -0
  88. package/src/templates/data-analyst/canvas.html +15 -0
  89. package/src/templates/data-analyst/metadata.json +39 -0
  90. package/src/templates/data-analyst/sample-profile.json +86 -0
  91. package/src/templates/data-analyst/style.css +285 -0
  92. package/src/templates/executive/README.md +9 -0
  93. package/src/templates/executive/canvas.html +15 -0
  94. package/src/templates/executive/sample-profile.json +86 -0
  95. package/src/templates/executive/style.css +58 -6
  96. package/src/templates/international-flow/README.md +26 -0
  97. package/src/templates/international-flow/canvas.html +21 -0
  98. package/src/templates/international-flow/metadata.json +37 -0
  99. package/src/templates/international-flow/sample-profile.json +86 -0
  100. package/src/templates/international-flow/style.css +208 -0
  101. package/src/templates/minimal/README.md +12 -1
  102. package/src/templates/minimal/canvas.html +21 -0
  103. package/src/templates/minimal/sample-profile.json +86 -0
  104. package/src/templates/minimal/style.css +24 -0
  105. package/src/templates/modern/README.md +9 -0
  106. package/src/templates/modern/canvas.html +15 -0
  107. package/src/templates/modern/sample-profile.json +86 -0
  108. package/src/templates/modern/style.css +48 -1
  109. package/src/templates/startup-generalist/README.md +25 -0
  110. package/src/templates/startup-generalist/canvas.html +21 -0
  111. package/src/templates/startup-generalist/metadata.json +37 -0
  112. package/src/templates/startup-generalist/sample-profile.json +86 -0
  113. package/src/templates/startup-generalist/style.css +237 -0
  114. package/src/templates/classic/template.html +0 -133
  115. package/src/templates/executive/template.html +0 -101
  116. package/src/templates/minimal/template.html +0 -114
  117. package/src/templates/modern/template.html +0 -139
@@ -1,162 +1,37 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- KnowMe CareerForge — Multi-Strategy Deterministic PDF Renderer
4
- 多策略自愈式 A4 PDF 渲染器:自动嗅探 Playwright、系统级 Chromium/Chrome/Edge/Brave 浏览器并实现零配置确定性导出。
5
- """
6
-
7
- import sys, os, subprocess, platform, shutil, argparse
2
+ """Render and verify a fresh PDF through browser-engine.js before publication."""
3
+ import argparse
4
+ import json
5
+ import sys
8
6
  from pathlib import Path
9
- from typing import Optional, List
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
- def find_system_browser() -> Optional[str]:
20
- system = platform.system()
21
-
22
- custom_bin = os.environ.get("CHROME_BIN") or os.environ.get("BROWSER_PATH")
23
- if custom_bin and Path(custom_bin).exists():
24
- return custom_bin
25
-
26
- candidates: List[str] = []
27
-
28
- if system == "Darwin": # macOS
29
- candidates = [
30
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
31
- "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
32
- "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
33
- "/Applications/Chromium.app/Contents/MacOS/Chromium",
34
- str(Path.home() / "Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
35
- str(Path.home() / "Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"),
36
- ]
37
- elif system == "Linux":
38
- candidates = [
39
- "google-chrome", "google-chrome-stable", "chromium", "chromium-browser",
40
- "/usr/bin/google-chrome", "/usr/bin/google-chrome-stable",
41
- "/usr/bin/chromium", "/usr/bin/chromium-browser",
42
- "/snap/bin/chromium"
43
- ]
44
- elif system == "Windows":
45
- prog_files = os.environ.get("PROGRAMFILES", r"C:\Program Files")
46
- prog_files_x86 = os.environ.get("PROGRAMFILES(X86)", r"C:\Program Files (x86)")
47
- local_app = os.environ.get("LOCALAPPDATA", "")
48
-
49
- candidates = [
50
- os.path.join(prog_files, r"Google\Chrome\Application\chrome.exe"),
51
- os.path.join(prog_files_x86, r"Google\Chrome\Application\chrome.exe"),
52
- os.path.join(prog_files_x86, r"Microsoft\Edge\Application\msedge.exe"),
53
- os.path.join(prog_files, r"Microsoft\Edge\Application\msedge.exe"),
54
- os.path.join(local_app, r"Google\Chrome\Application\chrome.exe"),
55
- os.path.join(local_app, r"Microsoft\Edge\Application\msedge.exe"),
56
- os.path.join(local_app, r"BraveSoftware\Brave-Browser\Application\brave.exe"),
57
- ]
58
-
59
- for candidate in candidates:
60
- if candidate.startswith("/") or ":" in candidate:
61
- if Path(candidate).exists():
62
- return candidate
63
- else:
64
- resolved = shutil.which(candidate)
65
- if resolved:
66
- return resolved
67
-
68
- return None
69
-
70
- def render_via_playwright(html_path: Path, pdf_path: Path) -> bool:
71
- root = get_project_root()
72
- script_ts = root / "scripts" / "rendering" / "render-pdf.ts"
73
- if not script_ts.exists():
74
- script_ts = root / "scripts" / "render-pdf.ts"
7
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
8
+ from browser_engine import run, exit_code
75
9
 
76
- if script_ts.exists() and shutil.which("npx"):
77
- try:
78
- cmd = ["npx", "ts-node", str(script_ts), "--input", str(html_path), "--output", str(pdf_path)]
79
- res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=30)
80
- if res.returncode == 0 and pdf_path.exists():
81
- return True
82
- except Exception:
83
- pass
84
- return False
85
10
 
86
- def render_via_browser(browser_bin: str, html_path: Path, pdf_path: Path) -> bool:
87
- html_url = html_path.resolve().as_uri()
88
- cmd = [
89
- browser_bin,
90
- "--headless=new",
91
- "--disable-gpu",
92
- "--no-first-run",
93
- "--no-default-browser-check",
94
- "--disable-background-networking",
95
- "--no-pdf-header-footer",
96
- "--run-all-compositor-stages-before-draw",
97
- f"--print-to-pdf={pdf_path.resolve()}",
98
- html_url
99
- ]
100
- try:
101
- res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=25)
102
- return pdf_path.exists() and pdf_path.stat().st_size > 0
103
- except Exception as e:
104
- print(f"[!] Browser render failed: {e}")
105
- return False
11
+ def render_pdf(html_path_str, pdf_path_str, quiet=False, expected_pages=1, auto_heal=False):
12
+ result = run(html_path_str, expected_pages=expected_pages,
13
+ auto_heal=auto_heal, output_pdf=pdf_path_str)
14
+ if result["status"] != "PASS":
15
+ raise RuntimeError(json.dumps(result, ensure_ascii=False))
16
+ return Path(pdf_path_str).resolve()
106
17
 
107
- def render_pdf(html_path_str: str, pdf_path_str: str, quiet: bool = False) -> Path:
108
- html_path = Path(html_path_str).resolve()
109
- pdf_path = Path(pdf_path_str).resolve()
110
-
111
- if not html_path.exists():
112
- raise FileNotFoundError(f"Input HTML file does not exist: {html_path}")
113
-
114
- pdf_path.parent.mkdir(parents=True, exist_ok=True)
115
-
116
- if not quiet:
117
- print("==============================================================")
118
- print(" KnowMe CareerForge — Multi-Strategy PDF Renderer")
119
- print(f" Source Canvas : {html_path}")
120
- print(f" Target PDF : {pdf_path}")
121
- print("==============================================================")
122
-
123
- # Strategy 1: Playwright Headless
124
- if not quiet:
125
- print("[*] Strategy 1: Trying Playwright Headless Engine...")
126
- if render_via_playwright(html_path, pdf_path):
127
- if not quiet:
128
- print(f"[✓] Render Succeeded via Playwright -> {pdf_path}")
129
- return pdf_path
130
-
131
- # Strategy 2: System Installed Chromium/Chrome/Edge/Brave
132
- browser_bin = find_system_browser()
133
- if browser_bin:
134
- if not quiet:
135
- print(f"[*] Strategy 2: Found system browser at: {browser_bin}")
136
- if render_via_browser(browser_bin, html_path, pdf_path):
137
- if not quiet:
138
- print(f"[✓] Render Succeeded via System Browser -> {pdf_path}")
139
- return pdf_path
140
-
141
- raise RuntimeError("No suitable PDF rendering engine found. Please install Playwright or Google Chrome / Edge.")
142
18
 
143
19
  def main():
144
- parser = argparse.ArgumentParser(description="KnowMe CareerForge — Multi-Strategy PDF Renderer")
145
- parser.add_argument("html", nargs="?", default="workspace/resume.html", help="Input HTML canvas path")
146
- parser.add_argument("pdf", nargs="?", default="workspace/resume.pdf", help="Output PDF path")
147
- parser.add_argument("--input", "-i", help="Input HTML (named option)")
148
- parser.add_argument("--output", "-o", help="Output PDF (named option)")
149
- parser.add_argument("--quiet", "-q", action="store_true", help="Quiet execution mode")
150
-
20
+ parser = argparse.ArgumentParser(description=__doc__)
21
+ parser.add_argument("html", nargs="?", default="workspace/resume.html")
22
+ parser.add_argument("pdf", nargs="?", default="workspace/resume.pdf")
23
+ parser.add_argument("--input", "-i")
24
+ parser.add_argument("--output", "-o")
25
+ parser.add_argument("--expected-pages", "-p", type=int, default=1)
26
+ parser.add_argument("--auto-heal", action="store_true")
27
+ parser.add_argument("--quiet", "-q", action="store_true")
28
+ parser.add_argument("--json", action="store_true")
151
29
  args = parser.parse_args()
152
- input_html = args.input or args.html
153
- output_pdf = args.output or args.pdf
30
+ result = run(args.input or args.html, expected_pages=args.expected_pages,
31
+ auto_heal=args.auto_heal, output_pdf=args.output or args.pdf)
32
+ print(json.dumps(result, ensure_ascii=False))
33
+ return exit_code(result)
154
34
 
155
- try:
156
- render_pdf(input_html, output_pdf, quiet=args.quiet)
157
- except Exception as e:
158
- print(f"[✗] PDF Generation Failed: {e}", file=sys.stderr)
159
- sys.exit(1)
160
35
 
161
36
  if __name__ == "__main__":
162
- main()
37
+ raise SystemExit(main())
@@ -1,86 +1,10 @@
1
1
  #!/usr/bin/env ts-node
2
- /**
3
- * KnowMe CareerForge — Playwright Deterministic PDF Renderer
4
- * 加载自包含 HTML 中间工作区,等待字体及样式准备就绪,输出标准像素级 A4 PDF。
5
- */
2
+ import engine = require('./engine-loader');
3
+ import type { EngineOptions, EngineResult } from './browser-engine';
6
4
 
7
- import { chromium } from 'playwright';
8
- import * as path from 'path';
9
- import * as fs from 'fs';
10
-
11
- interface RenderOptions {
12
- inputHtml: string;
13
- outputPdf: string;
14
- }
15
-
16
- export async function renderResumePdf(options: RenderOptions): Promise<void> {
17
- const absoluteHtml = path.resolve(options.inputHtml);
18
- const absolutePdf = path.resolve(options.outputPdf);
19
-
20
- if (!fs.existsSync(absoluteHtml)) {
21
- throw new Error(`Input HTML file does not exist: ${absoluteHtml}`);
22
- }
23
-
24
- const outputDir = path.dirname(absolutePdf);
25
- if (!fs.existsSync(outputDir)) {
26
- fs.mkdirSync(outputDir, { recursive: true });
27
- }
28
-
29
- console.log('======================================================');
30
- console.log(' KnowMe CareerForge — Playwright PDF Renderer');
31
- console.log(` Source Canvas : ${absoluteHtml}`);
32
- console.log(` Target PDF : ${absolutePdf}`);
33
- console.log('======================================================');
34
-
35
- const browser = await chromium.launch({ headless: true });
36
- const context = await browser.newContext({
37
- viewport: { width: 794, height: 1123 }, // A4 at 96 DPI
38
- deviceScaleFactor: 2
39
- });
40
-
41
- const page = await context.newPage();
42
- await page.goto(`file://${absoluteHtml}`, { waitUntil: 'networkidle' });
43
- await page.evaluateHandle('document.fonts.ready');
44
-
45
- await page.pdf({
46
- path: absolutePdf,
47
- format: 'A4',
48
- printBackground: true,
49
- preferCSSPageSize: true,
50
- margin: { top: '0px', right: '0px', bottom: '0px', left: '0px' }
51
- });
52
-
53
- await browser.close();
54
- console.log(`[✓] Deterministic PDF Rendered Successfully -> ${absolutePdf}`);
55
- }
56
-
57
- async function main() {
58
- const args = process.argv.slice(2);
59
- let inputHtml = 'workspace/resume.html';
60
- let outputPdf = 'workspace/resume.pdf';
61
-
62
- for (let i = 0; i < args.length; i++) {
63
- if (args[i] === '--input' || args[i] === '-i') {
64
- inputHtml = args[i + 1];
65
- i++;
66
- } else if (args[i] === '--output' || args[i] === '-o') {
67
- outputPdf = args[i + 1];
68
- i++;
69
- } else if (!args[i].startsWith('-')) {
70
- if (i === 0) inputHtml = args[i];
71
- else if (i === 1) outputPdf = args[i];
72
- }
73
- }
74
-
75
- try {
76
- await renderResumePdf({ inputHtml, outputPdf });
77
- } catch (err: unknown) {
78
- const errorMsg = err instanceof Error ? err.message : String(err);
79
- console.error(`[✗] PDF Rendering Failed: ${errorMsg}`);
80
- process.exit(1);
81
- }
82
- }
83
-
84
- if (require.main === module) {
85
- main();
5
+ export async function renderResumePdf(options: EngineOptions & { inputHtml: string; outputPdf: string }): Promise<EngineResult> {
6
+ const result = await engine.run(options.inputHtml, options);
7
+ if (result.status !== 'PASS') throw new Error(JSON.stringify(result));
8
+ return result;
86
9
  }
10
+ if (require.main === module) engine.cli(process.argv.slice(2), { outputPdf: 'workspace/resume.pdf' });