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.
- package/AGENT.md +276 -0
- package/ARCHITECTURE.md +220 -0
- package/README.md +46 -63
- package/README.zh-CN.md +47 -66
- package/SKILL.md +38 -135
- package/agents/claude/knowme-careerforge.md +2 -17
- package/agents/codex/knowme-careerforge.yaml +2 -20
- package/agents/cursor/knowme-careerforge.mdc +4 -13
- package/agents/gemini/knowme-careerforge.json +1 -1
- package/agents/opencode/skill.yaml +1 -1
- package/agents/windsurf/knowme-careerforge.rules +2 -12
- package/dist/cli/src/commands/init.js +51 -32
- package/dist/cli/src/index.js +25 -21
- package/dist/scripts/rendering/engine-loader.d.ts +2 -0
- package/dist/scripts/rendering/engine-loader.js +46 -0
- package/dist/scripts/rendering/render-pdf.d.ts +3 -8
- package/dist/scripts/rendering/render-pdf.js +7 -103
- package/dist/scripts/rendering/runtime.d.ts +9 -0
- package/dist/scripts/rendering/runtime.js +63 -0
- package/dist/scripts/validation/validate-ats.d.ts +2 -22
- package/dist/scripts/validation/validate-ats.js +6 -168
- package/dist/scripts/validation/validate-layout.d.ts +2 -23
- package/dist/scripts/validation/validate-layout.js +5 -159
- package/package.json +15 -4
- package/references/01-evidence-mining.md +35 -0
- package/references/02-career-goal.md +40 -0
- package/references/03-jd-analysis.md +32 -0
- package/references/04-template-selection.md +30 -0
- package/references/05-html-canvas-tokens.md +12 -0
- package/references/06-qa-and-rendering.md +27 -0
- package/references/07-artifact-contract.md +83 -0
- package/scripts/Agent.md +9 -136
- package/scripts/build/build-gallery.py +66 -56
- package/scripts/build/run-all-tests.py +1 -0
- package/scripts/build/sync-version.py +27 -9
- package/scripts/contracts/profile.py +101 -0
- package/scripts/evidence/extract-evidence.py +109 -62
- package/scripts/pipeline/forge.py +276 -136
- package/scripts/rendering/README.md +65 -0
- package/scripts/rendering/browser-engine.d.ts +15 -0
- package/scripts/rendering/browser-engine.js +408 -0
- package/scripts/rendering/browser_engine.py +39 -0
- package/scripts/rendering/engine-loader.ts +11 -0
- package/scripts/rendering/render-pdf.py +26 -151
- package/scripts/rendering/render-pdf.ts +7 -83
- package/scripts/template/instantiate-resume.py +266 -173
- package/scripts/template/search-template.py +89 -3
- package/scripts/validation/measure-dom.js +5 -0
- package/scripts/validation/validate-ats.ts +6 -174
- package/scripts/validation/validate-layout.ts +5 -159
- package/scripts/validation/validate-resume.py +23 -134
- package/skill.json +7 -6
- package/src/knowledge/index.json +391 -3
- package/src/knowledge/resume-schema.json +377 -71
- package/src/knowledge/templates.json +389 -1
- package/src/references/01-evidence-mining.md +24 -30
- package/src/references/02-career-goal.md +4 -2
- package/src/references/03-jd-analysis.md +5 -17
- package/src/references/04-template-selection.md +20 -33
- package/src/references/05-html-canvas-tokens.md +6 -45
- package/src/references/06-qa-and-rendering.md +16 -43
- package/src/references/07-artifact-contract.md +83 -0
- package/src/templates/Agent.md +11 -0
- package/src/templates/academic-research/README.md +25 -0
- package/src/templates/academic-research/canvas.html +21 -0
- package/src/templates/academic-research/metadata.json +38 -0
- package/src/templates/academic-research/sample-profile.json +86 -0
- package/src/templates/academic-research/style.css +213 -0
- package/src/templates/classic/README.md +9 -0
- package/src/templates/classic/canvas.html +21 -0
- package/src/templates/classic/metadata.json +3 -0
- package/src/templates/classic/sample-profile.json +86 -0
- package/src/templates/classic/style.css +14 -4
- package/src/templates/common/base.css +32 -0
- package/src/templates/common/canvas-bindings.css +41 -0
- package/src/templates/common/resume-contract.md +8 -0
- package/src/templates/compact-dense/README.md +25 -0
- package/src/templates/compact-dense/canvas.html +14 -0
- package/src/templates/compact-dense/metadata.json +38 -0
- package/src/templates/compact-dense/sample-profile.json +86 -0
- package/src/templates/compact-dense/style.css +218 -0
- package/src/templates/creative-tech/README.md +26 -0
- package/src/templates/creative-tech/canvas.html +15 -0
- package/src/templates/creative-tech/metadata.json +39 -0
- package/src/templates/creative-tech/sample-profile.json +86 -0
- package/src/templates/creative-tech/style.css +257 -0
- package/src/templates/data-analyst/README.md +26 -0
- package/src/templates/data-analyst/canvas.html +15 -0
- package/src/templates/data-analyst/metadata.json +39 -0
- package/src/templates/data-analyst/sample-profile.json +86 -0
- package/src/templates/data-analyst/style.css +285 -0
- package/src/templates/executive/README.md +9 -0
- package/src/templates/executive/canvas.html +15 -0
- package/src/templates/executive/sample-profile.json +86 -0
- package/src/templates/executive/style.css +58 -6
- package/src/templates/international-flow/README.md +26 -0
- package/src/templates/international-flow/canvas.html +21 -0
- package/src/templates/international-flow/metadata.json +37 -0
- package/src/templates/international-flow/sample-profile.json +86 -0
- package/src/templates/international-flow/style.css +208 -0
- package/src/templates/minimal/README.md +12 -1
- package/src/templates/minimal/canvas.html +21 -0
- package/src/templates/minimal/sample-profile.json +86 -0
- package/src/templates/minimal/style.css +24 -0
- package/src/templates/modern/README.md +9 -0
- package/src/templates/modern/canvas.html +15 -0
- package/src/templates/modern/sample-profile.json +86 -0
- package/src/templates/modern/style.css +48 -1
- package/src/templates/startup-generalist/README.md +25 -0
- package/src/templates/startup-generalist/canvas.html +21 -0
- package/src/templates/startup-generalist/metadata.json +37 -0
- package/src/templates/startup-generalist/sample-profile.json +86 -0
- package/src/templates/startup-generalist/style.css +237 -0
- package/src/templates/classic/template.html +0 -133
- package/src/templates/executive/template.html +0 -101
- package/src/templates/minimal/template.html +0 -114
- package/src/templates/modern/template.html +0 -139
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
name: knowme-careerforge
|
|
2
2
|
description: An agent-native skill for self-discovery, career positioning, and tailored resume engineering.
|
|
3
|
-
version: 0.0.
|
|
3
|
+
version: 0.0.7
|
|
4
4
|
entrypoint: SKILL.md
|
|
5
|
-
tools:
|
|
6
|
-
- name: forge
|
|
7
|
-
command: python3 scripts/pipeline/forge.py
|
|
8
|
-
- name: extract-evidence
|
|
9
|
-
command: python3 scripts/evidence/extract-evidence.py
|
|
10
|
-
- name: search-template
|
|
11
|
-
command: python3 scripts/template/search-template.py
|
|
12
|
-
- name: analyze-jd
|
|
13
|
-
command: python3 scripts/evidence/analyze-jd.py
|
|
14
|
-
- name: instantiate-resume
|
|
15
|
-
command: python3 scripts/template/instantiate-resume.py
|
|
16
|
-
- name: validate-resume
|
|
17
|
-
command: python3 scripts/validation/validate-resume.py
|
|
18
|
-
- name: render-pdf
|
|
19
|
-
command: python3 scripts/rendering/render-pdf.py
|
|
20
5
|
rules:
|
|
21
|
-
- "
|
|
22
|
-
- "Use workspace/resume.html as the sole Intermediate Working Canvas."
|
|
23
|
-
- "Deliver final concise summary and path to workspace/resume.pdf."
|
|
24
|
-
- "Reference AGENT.md, ARCHITECTURE.md, and references/01~06 for complete specifications."
|
|
6
|
+
- "Read SKILL.md at the installed skill root and follow it as the single runtime workflow. Resolve its scripts and references from that directory, not the candidate workspace. Load supporting references only when the current step needs them. Do not load AGENT.md or architecture documents to make a resume."
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
3
|
-
|
|
2
|
+
description: Resume drafting and verified delivery from user-provided facts
|
|
3
|
+
alwaysApply: false
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# KnowMe CareerForge
|
|
6
|
+
# KnowMe CareerForge
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
1. **Evidence-First Invariant**: Enforce strict L1~L3 evidence classification (see `references/01-evidence-mining.md`). Never hallucinate ungrounded revenue metrics, fake degrees, or false tenures.
|
|
11
|
-
2. **Repo-to-Resume Extraction**: Extract codebase facts using `python3 scripts/evidence/extract-evidence.py --repo .` or execute the one-shot pipeline `python3 scripts/pipeline/forge.py`.
|
|
12
|
-
3. **Target Alignment & JD Parsing**: Analyze target Job Descriptions using `python3 scripts/evidence/analyze-jd.py` (see `references/03-jd-analysis.md`).
|
|
13
|
-
4. **Template Search**: Match template via `python3 scripts/template/search-template.py "<Target Role>"` (see `references/04-template-selection.md`).
|
|
14
|
-
5. **HTML Canvas Editing**: Edit exclusively in `workspace/resume.html` Intermediate Working Canvas (`scripts/template/instantiate-resume.py`). Calibrate CSS variables (`--resume-*`) in `:root` (see `references/05-html-canvas-tokens.md`).
|
|
15
|
-
6. **Dual QA & Deterministic PDF**: Validate layout height ($297\text{mm} \le 1122.5\text{px}$) and ATS via `python3 scripts/validation/validate-resume.py`. Export final vector PDF via `python3 scripts/rendering/render-pdf.py`.
|
|
16
|
-
7. **Quiet Execution Protocol**: Execute all tools silently with `--quiet`. Never output raw HTML/CSS into the chat. Deliver concise value proposition and direct output file paths.
|
|
17
|
-
8. **Authoritative Guide**: Refer to root `AGENT.md` for Vercel-inspired editorial standards and `ARCHITECTURE.md` for system design.
|
|
8
|
+
Read SKILL.md at the installed skill root and follow it as the single runtime workflow. Resolve its scripts and references from that directory, not the candidate workspace. Load supporting references only when the current step needs them. Do not load AGENT.md or architecture documents to make a resume.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knowme-careerforge",
|
|
3
3
|
"description": "An agent-native skill for self-discovery, career positioning, and tailored resume engineering.",
|
|
4
|
-
"instructions": "
|
|
4
|
+
"instructions": "Read SKILL.md at the installed skill root and follow it as the single runtime workflow. Resolve its scripts and references from that directory, not the candidate workspace. Load supporting references only when the current step needs them. Do not load AGENT.md or architecture documents to make a resume.",
|
|
5
5
|
"entrypoint": "SKILL.md"
|
|
6
6
|
}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
# KnowMe CareerForge
|
|
2
|
-
# Trigger: Career discovery, resume tailoring, JD analysis, repo-to-resume
|
|
1
|
+
# KnowMe CareerForge
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
1. **Evidence Mining**: Extract candidate facts using `python3 scripts/evidence/extract-evidence.py --repo .` or execute one-shot `python3 scripts/pipeline/forge.py`.
|
|
6
|
-
2. **Anti-Hallucination Gate**: Tag all experience items with Evidence Level (L1~L3 only, see `references/01-evidence-mining.md`).
|
|
7
|
-
3. **JD & Signal Analysis**: Analyze target JD requirements using `python3 scripts/evidence/analyze-jd.py` (see `references/03-jd-analysis.md`).
|
|
8
|
-
4. **Template Search**: Recommend best template using `python3 scripts/template/search-template.py` (see `references/04-template-selection.md`).
|
|
9
|
-
5. **Canvas Instantiation**: Instantiate `workspace/resume.html` using `python3 scripts/template/instantiate-resume.py` and calibrate CSS design tokens (see `references/05-html-canvas-tokens.md`).
|
|
10
|
-
6. **Dual QA Verification**: Validate DOM fit ($297\text{mm} \le 1122.5\text{px}$) and ATS compatibility using `python3 scripts/validation/validate-resume.py`.
|
|
11
|
-
7. **Deterministic PDF Export**: Export final vector PDF using `python3 scripts/rendering/render-pdf.py` (see `references/06-qa-and-rendering.md`).
|
|
12
|
-
8. **Quiet Delivery**: Execute silently in background (`--quiet`); deliver concise value proposition and direct output file paths (`workspace/resume.pdf`).
|
|
13
|
-
9. **Authoritative Guide**: Refer to root `AGENT.md` for editorial standards and `ARCHITECTURE.md` for system architecture.
|
|
3
|
+
Read SKILL.md at the installed skill root and follow it as the single runtime workflow. Resolve its scripts and references from that directory, not the candidate workspace. Load supporting references only when the current step needs them. Do not load AGENT.md or architecture documents to make a resume.
|
|
@@ -70,6 +70,25 @@ function copyDirRecursive(src, dest) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
function copyRuntimeBundle(rootDir, destination) {
|
|
74
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
75
|
+
for (const directory of ['scripts', 'src', 'references']) {
|
|
76
|
+
copyDirRecursive(path.join(rootDir, directory), path.join(destination, directory));
|
|
77
|
+
}
|
|
78
|
+
for (const file of ['SKILL.md', 'skill.json', 'package.json']) {
|
|
79
|
+
fs.copyFileSync(path.join(rootDir, file), path.join(destination, file));
|
|
80
|
+
}
|
|
81
|
+
// npm archives omit package-lock.json; source checkouts can retain a locked install.
|
|
82
|
+
const lock = path.join(rootDir, 'package-lock.json');
|
|
83
|
+
const installedLock = path.join(destination, 'package-lock.json');
|
|
84
|
+
if (fs.existsSync(lock))
|
|
85
|
+
fs.copyFileSync(lock, installedLock);
|
|
86
|
+
else
|
|
87
|
+
fs.rmSync(installedLock, { force: true });
|
|
88
|
+
console.log(`Runtime files copied -> ${destination}`);
|
|
89
|
+
console.log(`Prepare dependencies in that directory: npm ${fs.existsSync(lock) ? 'ci' : 'install'} --omit=dev`);
|
|
90
|
+
console.log('Then run: node scripts/rendering/browser-engine.js --check-runtime');
|
|
91
|
+
}
|
|
73
92
|
function runInit(options) {
|
|
74
93
|
const rootDir = findPackageRoot(__dirname);
|
|
75
94
|
const projectDir = options.projectDir ? path.resolve(options.projectDir) : process.cwd();
|
|
@@ -85,82 +104,82 @@ function runInit(options) {
|
|
|
85
104
|
console.log(` Target Project: ${projectDir}`);
|
|
86
105
|
console.log(` Platforms : ${targetPlatforms.join(', ')}`);
|
|
87
106
|
console.log('==============================================================');
|
|
107
|
+
let failures = 0;
|
|
88
108
|
for (const plat of targetPlatforms) {
|
|
89
109
|
try {
|
|
90
110
|
switch (plat) {
|
|
91
111
|
case 'claude': {
|
|
92
112
|
const claudeDir = path.join(homeDir, '.claude', 'skills', 'knowme-careerforge');
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
copyDirRecursive(path.join(rootDir, 'src'), path.join(claudeDir, 'src'));
|
|
96
|
-
copyDirRecursive(path.join(rootDir, 'references'), path.join(claudeDir, 'references'));
|
|
97
|
-
fs.copyFileSync(path.join(rootDir, 'SKILL.md'), path.join(claudeDir, 'SKILL.md'));
|
|
98
|
-
fs.copyFileSync(path.join(rootDir, 'AGENT.md'), path.join(claudeDir, 'AGENT.md'));
|
|
99
|
-
fs.copyFileSync(path.join(rootDir, 'ARCHITECTURE.md'), path.join(claudeDir, 'ARCHITECTURE.md'));
|
|
100
|
-
fs.copyFileSync(path.join(rootDir, 'skill.json'), path.join(claudeDir, 'skill.json'));
|
|
101
|
-
console.log(`[✓] Claude Code Skill fully bundled -> ${claudeDir}`);
|
|
113
|
+
copyRuntimeBundle(rootDir, claudeDir);
|
|
114
|
+
console.log(`[✓] Claude Code Skill files installed -> ${claudeDir}`);
|
|
102
115
|
break;
|
|
103
116
|
}
|
|
104
117
|
case 'codex': {
|
|
105
118
|
const codexDir = path.join(homeDir, '.codex', 'skills', 'knowme-careerforge');
|
|
106
|
-
|
|
107
|
-
copyDirRecursive(path.join(rootDir, 'scripts'), path.join(codexDir, 'scripts'));
|
|
108
|
-
copyDirRecursive(path.join(rootDir, 'src'), path.join(codexDir, 'src'));
|
|
109
|
-
copyDirRecursive(path.join(rootDir, 'references'), path.join(codexDir, 'references'));
|
|
110
|
-
fs.copyFileSync(path.join(rootDir, 'SKILL.md'), path.join(codexDir, 'SKILL.md'));
|
|
111
|
-
fs.copyFileSync(path.join(rootDir, 'AGENT.md'), path.join(codexDir, 'AGENT.md'));
|
|
112
|
-
fs.copyFileSync(path.join(rootDir, 'ARCHITECTURE.md'), path.join(codexDir, 'ARCHITECTURE.md'));
|
|
113
|
-
fs.copyFileSync(path.join(rootDir, 'skill.json'), path.join(codexDir, 'skill.json'));
|
|
119
|
+
copyRuntimeBundle(rootDir, codexDir);
|
|
114
120
|
if (fs.existsSync(path.join(rootDir, 'agents', 'codex', 'knowme-careerforge.yaml'))) {
|
|
115
121
|
fs.copyFileSync(path.join(rootDir, 'agents', 'codex', 'knowme-careerforge.yaml'), path.join(codexDir, 'skill.yaml'));
|
|
116
122
|
}
|
|
117
|
-
console.log(`[✓] Codex Native Skill
|
|
123
|
+
console.log(`[✓] Codex Native Skill files installed -> ${codexDir}`);
|
|
118
124
|
break;
|
|
119
125
|
}
|
|
120
126
|
case 'cursor': {
|
|
121
127
|
const cursorRulesDir = path.join(projectDir, '.cursor', 'rules');
|
|
122
128
|
fs.mkdirSync(cursorRulesDir, { recursive: true });
|
|
129
|
+
const runtimeDir = path.join(projectDir, '.knowme', 'skills', 'knowme-careerforge');
|
|
130
|
+
copyRuntimeBundle(rootDir, runtimeDir);
|
|
123
131
|
const mdcContent = fs.readFileSync(path.join(rootDir, 'agents', 'cursor', 'knowme-careerforge.mdc'), 'utf-8');
|
|
124
|
-
fs.writeFileSync(path.join(cursorRulesDir, 'knowme-careerforge.mdc'), mdcContent, 'utf-8');
|
|
132
|
+
fs.writeFileSync(path.join(cursorRulesDir, 'knowme-careerforge.mdc'), mdcContent + `\nRuntime entry: ${JSON.stringify(path.join(runtimeDir, 'SKILL.md'))}\n`, 'utf-8');
|
|
125
133
|
console.log(`[✓] Cursor MDC Rule configured -> ${path.join(cursorRulesDir, 'knowme-careerforge.mdc')}`);
|
|
126
134
|
break;
|
|
127
135
|
}
|
|
128
136
|
case 'windsurf': {
|
|
129
137
|
const windsurfRulePath = path.join(projectDir, '.windsurfrules');
|
|
138
|
+
const runtimeDir = path.join(projectDir, '.knowme', 'skills', 'knowme-careerforge');
|
|
139
|
+
copyRuntimeBundle(rootDir, runtimeDir);
|
|
130
140
|
const ruleContent = fs.readFileSync(path.join(rootDir, 'agents', 'windsurf', 'knowme-careerforge.rules'), 'utf-8');
|
|
131
|
-
|
|
141
|
+
const begin = '<!-- knowme-careerforge:start -->';
|
|
142
|
+
const end = '<!-- knowme-careerforge:end -->';
|
|
143
|
+
const previous = fs.existsSync(windsurfRulePath) ? fs.readFileSync(windsurfRulePath, 'utf-8') : '';
|
|
144
|
+
const block = `${begin}\n${ruleContent}\nRuntime entry: ${JSON.stringify(path.join(runtimeDir, 'SKILL.md'))}\n${end}`;
|
|
145
|
+
const managed = /<!-- knowme-careerforge:start -->[\s\S]*?<!-- knowme-careerforge:end -->/;
|
|
146
|
+
fs.writeFileSync(windsurfRulePath, managed.test(previous) ? previous.replace(managed, () => block) : previous + `\n\n${block}\n`, 'utf-8');
|
|
132
147
|
console.log(`[✓] Windsurf Rule configured -> ${windsurfRulePath}`);
|
|
133
148
|
break;
|
|
134
149
|
}
|
|
135
150
|
case 'gemini': {
|
|
136
151
|
const geminiDir = path.join(homeDir, '.gemini', 'skills');
|
|
137
152
|
fs.mkdirSync(geminiDir, { recursive: true });
|
|
138
|
-
const
|
|
139
|
-
|
|
153
|
+
const runtimeDir = path.join(geminiDir, 'knowme-careerforge');
|
|
154
|
+
copyRuntimeBundle(rootDir, runtimeDir);
|
|
155
|
+
const geminiConfig = JSON.parse(fs.readFileSync(path.join(rootDir, 'agents', 'gemini', 'knowme-careerforge.json'), 'utf-8'));
|
|
156
|
+
geminiConfig.entrypoint = path.join(runtimeDir, 'SKILL.md');
|
|
157
|
+
fs.writeFileSync(path.join(geminiDir, 'knowme-careerforge.json'), JSON.stringify(geminiConfig, null, 2) + '\n', 'utf-8');
|
|
140
158
|
console.log(`[✓] Gemini CLI Skill configured -> ${path.join(geminiDir, 'knowme-careerforge.json')}`);
|
|
141
159
|
break;
|
|
142
160
|
}
|
|
143
161
|
case 'opencode': {
|
|
144
162
|
const opencodeDir = path.join(projectDir, '.opencode', 'skills', 'knowme-careerforge');
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
copyDirRecursive(path.join(rootDir, 'src'), path.join(opencodeDir, 'src'));
|
|
148
|
-
copyDirRecursive(path.join(rootDir, 'references'), path.join(opencodeDir, 'references'));
|
|
149
|
-
fs.copyFileSync(path.join(rootDir, 'SKILL.md'), path.join(opencodeDir, 'SKILL.md'));
|
|
150
|
-
fs.copyFileSync(path.join(rootDir, 'AGENT.md'), path.join(opencodeDir, 'AGENT.md'));
|
|
151
|
-
fs.copyFileSync(path.join(rootDir, 'ARCHITECTURE.md'), path.join(opencodeDir, 'ARCHITECTURE.md'));
|
|
152
|
-
fs.copyFileSync(path.join(rootDir, 'skill.json'), path.join(opencodeDir, 'skill.json'));
|
|
153
|
-
console.log(`[✓] OpenCode Skill fully bundled -> ${opencodeDir}`);
|
|
163
|
+
copyRuntimeBundle(rootDir, opencodeDir);
|
|
164
|
+
console.log(`[✓] OpenCode Skill files installed -> ${opencodeDir}`);
|
|
154
165
|
break;
|
|
155
166
|
}
|
|
156
167
|
default:
|
|
168
|
+
failures += 1;
|
|
157
169
|
console.log(`[!] Unknown platform: ${plat}. Supported: ${SUPPORTED_PLATFORMS.join(', ')}`);
|
|
158
170
|
}
|
|
159
171
|
}
|
|
160
172
|
catch (err) {
|
|
173
|
+
failures += 1;
|
|
161
174
|
const msg = err instanceof Error ? err.message : String(err);
|
|
162
175
|
console.error(`[✗] Failed to initialize ${plat}: ${msg}`);
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
|
-
|
|
178
|
+
if (failures) {
|
|
179
|
+
process.exitCode = 1;
|
|
180
|
+
console.error(`[✗] ${failures} platform setup(s) failed.`);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log('Configuration and runtime files installed. Prepare dependencies and check runtime before generating PDFs; host discovery must be verified separately.');
|
|
184
|
+
}
|
|
166
185
|
}
|
package/dist/cli/src/index.js
CHANGED
|
@@ -58,11 +58,11 @@ function getVersion(rootDir) {
|
|
|
58
58
|
const pkgJson = path.join(rootDir, 'package.json');
|
|
59
59
|
if (require('fs').existsSync(pkgJson)) {
|
|
60
60
|
const data = JSON.parse(require('fs').readFileSync(pkgJson, 'utf-8'));
|
|
61
|
-
return data.version || '0.0.
|
|
61
|
+
return data.version || '0.0.7';
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
catch { }
|
|
65
|
-
return '0.0.
|
|
65
|
+
return '0.0.7';
|
|
66
66
|
}
|
|
67
67
|
function showHelp() {
|
|
68
68
|
console.log(`
|
|
@@ -74,8 +74,7 @@ Usage:
|
|
|
74
74
|
knowme <command> [options]
|
|
75
75
|
|
|
76
76
|
Commands:
|
|
77
|
-
forge
|
|
78
|
-
extract Extract candidate evidence & facts from codebase/Git repo
|
|
77
|
+
forge Isolated user facts -> variant -> canvas -> verified PDF
|
|
79
78
|
render Multi-strategy deterministic A4 PDF export
|
|
80
79
|
init Install and configure Skill for AI Agent platforms (cursor, claude, codex, windsurf, gemini, opencode)
|
|
81
80
|
list Display all available role profiles and HTML templates
|
|
@@ -86,17 +85,19 @@ Commands:
|
|
|
86
85
|
help Show this help message
|
|
87
86
|
|
|
88
87
|
Options (for forge):
|
|
89
|
-
--
|
|
88
|
+
--profile-json <file> User/Agent prepared facts or Master document
|
|
89
|
+
--workspace <dir> Parent for isolated run directories (default: workspace/runs)
|
|
90
|
+
--draft Create a draft canvas without PDF delivery
|
|
90
91
|
--role <title> Target role title
|
|
91
92
|
--jd <file|text> Target job description
|
|
92
|
-
--template, -t <id> Template ID (minimal, modern, executive, classic)
|
|
93
|
+
--template, -t <id> Template ID (minimal, modern, executive, classic, academic-research, international-flow, creative-tech, compact-dense, startup-generalist, data-analyst)
|
|
93
94
|
--name <name> Candidate name override
|
|
94
|
-
--output, -o <path>
|
|
95
|
+
--output, -o <path> Optional verified PDF copy (default: isolated run)
|
|
96
|
+
--font-preset <name> system or arial-unicode (requires local Arial Unicode MS)
|
|
97
|
+
--auto-heal Enable heuristic token auto-healing to eliminate A4 height overflow
|
|
95
98
|
--quiet, -q Quiet execution (compact JSON output)
|
|
96
|
-
|
|
97
99
|
Examples:
|
|
98
|
-
knowme forge --
|
|
99
|
-
knowme extract --repo . --output workspace/evidence-master.json
|
|
100
|
+
knowme forge --profile-json candidate.json --role "AI Agent Engineer" --template modern
|
|
100
101
|
knowme render workspace/resume.html workspace/resume.pdf
|
|
101
102
|
knowme init --ai codex
|
|
102
103
|
knowme init --all
|
|
@@ -118,13 +119,8 @@ function main() {
|
|
|
118
119
|
break;
|
|
119
120
|
}
|
|
120
121
|
case 'extract': {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
scriptPath = path.join(rootDir, 'scripts', 'extract-evidence.py');
|
|
124
|
-
const passArgs = args.slice(1);
|
|
125
|
-
const proc = child_process.spawnSync('python3', [scriptPath, ...passArgs], { stdio: 'inherit' });
|
|
126
|
-
if (proc.status !== 0)
|
|
127
|
-
process.exit(proc.status || 1);
|
|
122
|
+
console.error('Repository-to-resume extraction is not supported. Use user-provided facts with forge --profile-json.');
|
|
123
|
+
process.exitCode = 1;
|
|
128
124
|
break;
|
|
129
125
|
}
|
|
130
126
|
case 'render': {
|
|
@@ -165,7 +161,9 @@ function main() {
|
|
|
165
161
|
if (!require('fs').existsSync(scriptPath))
|
|
166
162
|
scriptPath = path.join(rootDir, 'scripts', 'search-template.py');
|
|
167
163
|
const passArgs = args.slice(1);
|
|
168
|
-
child_process.spawnSync('python3', [scriptPath, ...passArgs], { stdio: 'inherit' });
|
|
164
|
+
const proc = child_process.spawnSync('python3', [scriptPath, ...passArgs], { stdio: 'inherit' });
|
|
165
|
+
if (proc.status !== 0)
|
|
166
|
+
process.exit(proc.status || 1);
|
|
169
167
|
break;
|
|
170
168
|
}
|
|
171
169
|
case 'validate': {
|
|
@@ -173,21 +171,27 @@ function main() {
|
|
|
173
171
|
if (!require('fs').existsSync(scriptPath))
|
|
174
172
|
scriptPath = path.join(rootDir, 'scripts', 'validate-resume.py');
|
|
175
173
|
const passArgs = args.slice(1);
|
|
176
|
-
child_process.spawnSync('python3', [scriptPath, ...passArgs], { stdio: 'inherit' });
|
|
174
|
+
const proc = child_process.spawnSync('python3', [scriptPath, ...passArgs], { stdio: 'inherit' });
|
|
175
|
+
if (proc.status !== 0)
|
|
176
|
+
process.exit(proc.status || 1);
|
|
177
177
|
break;
|
|
178
178
|
}
|
|
179
179
|
case 'gallery': {
|
|
180
180
|
let scriptPath = path.join(rootDir, 'scripts', 'build', 'build-gallery.py');
|
|
181
181
|
if (!require('fs').existsSync(scriptPath))
|
|
182
182
|
scriptPath = path.join(rootDir, 'scripts', 'build-gallery.py');
|
|
183
|
-
child_process.spawnSync('python3', [scriptPath], { stdio: 'inherit' });
|
|
183
|
+
const proc = child_process.spawnSync('python3', [scriptPath], { stdio: 'inherit' });
|
|
184
|
+
if (proc.status !== 0)
|
|
185
|
+
process.exit(proc.status || 1);
|
|
184
186
|
break;
|
|
185
187
|
}
|
|
186
188
|
case 'test': {
|
|
187
189
|
let scriptPath = path.join(rootDir, 'scripts', 'build', 'run-all-tests.py');
|
|
188
190
|
if (!require('fs').existsSync(scriptPath))
|
|
189
191
|
scriptPath = path.join(rootDir, 'scripts', 'run-all-tests.py');
|
|
190
|
-
child_process.spawnSync('python3', [scriptPath], { stdio: 'inherit' });
|
|
192
|
+
const proc = child_process.spawnSync('python3', [scriptPath], { stdio: 'inherit' });
|
|
193
|
+
if (proc.status !== 0)
|
|
194
|
+
process.exit(proc.status || 1);
|
|
191
195
|
break;
|
|
192
196
|
}
|
|
193
197
|
case '--version':
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
const fs = __importStar(require("fs"));
|
|
36
|
+
const path = __importStar(require("path"));
|
|
37
|
+
// tsc does not copy JS. Resolve the shipped source engine from source or dist.
|
|
38
|
+
let root = __dirname;
|
|
39
|
+
while (!fs.existsSync(path.join(root, 'package.json'))) {
|
|
40
|
+
const parent = path.dirname(root);
|
|
41
|
+
if (parent === root)
|
|
42
|
+
throw new Error('Cannot locate browser engine project root');
|
|
43
|
+
root = parent;
|
|
44
|
+
}
|
|
45
|
+
const engine = require(path.join(root, 'scripts/rendering/browser-engine.js'));
|
|
46
|
+
module.exports = engine;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ts-node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 加载自包含 HTML 中间工作区,等待字体及样式准备就绪,输出标准像素级 A4 PDF。
|
|
5
|
-
*/
|
|
6
|
-
interface RenderOptions {
|
|
2
|
+
import type { EngineOptions, EngineResult } from './browser-engine';
|
|
3
|
+
export declare function renderResumePdf(options: EngineOptions & {
|
|
7
4
|
inputHtml: string;
|
|
8
5
|
outputPdf: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function renderResumePdf(options: RenderOptions): Promise<void>;
|
|
11
|
-
export {};
|
|
6
|
+
}): Promise<EngineResult>;
|
|
@@ -1,109 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env ts-node
|
|
2
2
|
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* KnowMe CareerForge — Playwright Deterministic PDF Renderer
|
|
5
|
-
* 加载自包含 HTML 中间工作区,等待字体及样式准备就绪,输出标准像素级 A4 PDF。
|
|
6
|
-
*/
|
|
7
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
-
if (k2 === undefined) k2 = k;
|
|
9
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
-
}
|
|
13
|
-
Object.defineProperty(o, k2, desc);
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
-
}) : function(o, v) {
|
|
21
|
-
o["default"] = v;
|
|
22
|
-
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
-
var ownKeys = function(o) {
|
|
25
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
-
var ar = [];
|
|
27
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
-
return ar;
|
|
29
|
-
};
|
|
30
|
-
return ownKeys(o);
|
|
31
|
-
};
|
|
32
|
-
return function (mod) {
|
|
33
|
-
if (mod && mod.__esModule) return mod;
|
|
34
|
-
var result = {};
|
|
35
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
-
__setModuleDefault(result, mod);
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
})();
|
|
40
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
4
|
exports.renderResumePdf = renderResumePdf;
|
|
42
|
-
const
|
|
43
|
-
const path = __importStar(require("path"));
|
|
44
|
-
const fs = __importStar(require("fs"));
|
|
5
|
+
const engine = require("./engine-loader");
|
|
45
6
|
async function renderResumePdf(options) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
const outputDir = path.dirname(absolutePdf);
|
|
52
|
-
if (!fs.existsSync(outputDir)) {
|
|
53
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
54
|
-
}
|
|
55
|
-
console.log('======================================================');
|
|
56
|
-
console.log(' KnowMe CareerForge — Playwright PDF Renderer');
|
|
57
|
-
console.log(` Source Canvas : ${absoluteHtml}`);
|
|
58
|
-
console.log(` Target PDF : ${absolutePdf}`);
|
|
59
|
-
console.log('======================================================');
|
|
60
|
-
const browser = await playwright_1.chromium.launch({ headless: true });
|
|
61
|
-
const context = await browser.newContext({
|
|
62
|
-
viewport: { width: 794, height: 1123 }, // A4 at 96 DPI
|
|
63
|
-
deviceScaleFactor: 2
|
|
64
|
-
});
|
|
65
|
-
const page = await context.newPage();
|
|
66
|
-
await page.goto(`file://${absoluteHtml}`, { waitUntil: 'networkidle' });
|
|
67
|
-
await page.evaluateHandle('document.fonts.ready');
|
|
68
|
-
await page.pdf({
|
|
69
|
-
path: absolutePdf,
|
|
70
|
-
format: 'A4',
|
|
71
|
-
printBackground: true,
|
|
72
|
-
preferCSSPageSize: true,
|
|
73
|
-
margin: { top: '0px', right: '0px', bottom: '0px', left: '0px' }
|
|
74
|
-
});
|
|
75
|
-
await browser.close();
|
|
76
|
-
console.log(`[✓] Deterministic PDF Rendered Successfully -> ${absolutePdf}`);
|
|
77
|
-
}
|
|
78
|
-
async function main() {
|
|
79
|
-
const args = process.argv.slice(2);
|
|
80
|
-
let inputHtml = 'workspace/resume.html';
|
|
81
|
-
let outputPdf = 'workspace/resume.pdf';
|
|
82
|
-
for (let i = 0; i < args.length; i++) {
|
|
83
|
-
if (args[i] === '--input' || args[i] === '-i') {
|
|
84
|
-
inputHtml = args[i + 1];
|
|
85
|
-
i++;
|
|
86
|
-
}
|
|
87
|
-
else if (args[i] === '--output' || args[i] === '-o') {
|
|
88
|
-
outputPdf = args[i + 1];
|
|
89
|
-
i++;
|
|
90
|
-
}
|
|
91
|
-
else if (!args[i].startsWith('-')) {
|
|
92
|
-
if (i === 0)
|
|
93
|
-
inputHtml = args[i];
|
|
94
|
-
else if (i === 1)
|
|
95
|
-
outputPdf = args[i];
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
await renderResumePdf({ inputHtml, outputPdf });
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
103
|
-
console.error(`[✗] PDF Rendering Failed: ${errorMsg}`);
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (require.main === module) {
|
|
108
|
-
main();
|
|
7
|
+
const result = await engine.run(options.inputHtml, options);
|
|
8
|
+
if (result.status !== 'PASS')
|
|
9
|
+
throw new Error(JSON.stringify(result));
|
|
10
|
+
return result;
|
|
109
11
|
}
|
|
12
|
+
if (require.main === module)
|
|
13
|
+
engine.cli(process.argv.slice(2), { outputPdf: 'workspace/resume.pdf' });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PrintInspection {
|
|
2
|
+
status: 'PASS' | 'FAIL' | 'UNVERIFIED';
|
|
3
|
+
errors: string[];
|
|
4
|
+
warnings?: string[];
|
|
5
|
+
pdfPages?: number;
|
|
6
|
+
actualHeightPx?: number;
|
|
7
|
+
measurementSource?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function inspectPrint(htmlPath: string, expectedPages?: number, outputPdf?: string): PrintInspection;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.inspectPrint = inspectPrint;
|
|
37
|
+
/** Typed bridge used by legacy TypeScript entry points and their compiled output. */
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
function inspectPrint(htmlPath, expectedPages = 1, outputPdf) {
|
|
42
|
+
let root = __dirname;
|
|
43
|
+
while (!fs.existsSync(path.join(root, 'SKILL.md'))) {
|
|
44
|
+
const parent = path.dirname(root);
|
|
45
|
+
if (parent === root)
|
|
46
|
+
throw new Error('Skill root not found');
|
|
47
|
+
root = parent;
|
|
48
|
+
}
|
|
49
|
+
const args = [path.join(root, 'scripts/rendering/browser-engine.js'), path.resolve(htmlPath), '--expected-pages', String(expectedPages)];
|
|
50
|
+
if (outputPdf)
|
|
51
|
+
args.push('--output', path.resolve(outputPdf));
|
|
52
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, args, { encoding: 'utf8', timeout: 120000 });
|
|
53
|
+
if (result.error)
|
|
54
|
+
throw result.error;
|
|
55
|
+
const parsed = JSON.parse(result.stdout);
|
|
56
|
+
if (!parsed || typeof parsed !== 'object' || !('status' in parsed) || !('errors' in parsed) || !Array.isArray(parsed.errors))
|
|
57
|
+
throw new Error('Invalid print inspection result');
|
|
58
|
+
if (parsed.status !== 'PASS' && parsed.status !== 'FAIL' && parsed.status !== 'UNVERIFIED')
|
|
59
|
+
throw new Error('Invalid print status');
|
|
60
|
+
if (result.status !== 0 && parsed.status === 'PASS')
|
|
61
|
+
throw new Error('Print engine exited unsuccessfully');
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
@@ -1,23 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env ts-node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 验证 HTML 简历中的关键文本可提取性、标头层级规范与 ATS 机器解析友好度。
|
|
5
|
-
*/
|
|
6
|
-
interface AtsValidationResult {
|
|
7
|
-
status: 'PASS' | 'WARN' | 'FAIL';
|
|
8
|
-
candidateName: string | null;
|
|
9
|
-
detectedContacts: {
|
|
10
|
-
email: string | null;
|
|
11
|
-
phone: string | null;
|
|
12
|
-
};
|
|
13
|
-
headingsFound: Array<{
|
|
14
|
-
level: string;
|
|
15
|
-
text: string;
|
|
16
|
-
isStandard: boolean;
|
|
17
|
-
}>;
|
|
18
|
-
totalTextLength: number;
|
|
19
|
-
warnings: string[];
|
|
20
|
-
errors: string[];
|
|
21
|
-
}
|
|
22
|
-
export declare function validateAts(htmlPath: string): Promise<AtsValidationResult>;
|
|
23
|
-
export {};
|
|
2
|
+
import type { EngineOptions, EngineResult } from '../rendering/browser-engine';
|
|
3
|
+
export declare function validateAts(htmlPath: string, expectedPages?: number, options?: EngineOptions): Promise<EngineResult>;
|