feat(cli): add power-user pack — Claude Code skill, commands, welcome#71
feat(cli): add power-user pack — Claude Code skill, commands, welcome#71amirbahador-hub wants to merge 15 commits into
Conversation
Shared, validated contract for CV Builder surfaces: Resume, JobDescription, Archetype, Issue, Claim, and EvalResult (with required rubric/archetype versions). Closes #47
Scoring brain the prompts reference: rubric v1 (six weighted dimensions with 0-5 anchors), three role archetypes (Software Engineer, Product Manager, Data & ML Engineer), keyword-based detectArchetype, and the ATS and claim validator specs. Closes #63
The three Phase 1 prompts a power user's agent runs. Markdown templates are the source of truth; renderers inject the live rubric, archetype weights, and claim rules so each assembled prompt is self-contained and stays in sync with the intelligence package. Every prompt asks for JSON matching its schema. Closes #64
Clone the repo, open Claude Code, run /evaluate-cv: the cv-evaluation skill runs extract -> detect -> score -> validate-claims locally, reading the prompts and rubric straight from the repo (no build). A SessionStart hook greets the user with the available commands on open. Skill subfiles point at the package sources to avoid drift. Closes #65
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds a local CV evaluation stack: Zod schemas, intelligence (rubric/archetypes/validators), prompt templates and renderers, Claude Code skill docs/commands, tests, and CLI docs to run an extract→detect→score→validate-claims pipeline fully locally. ChangesCV Evaluation Infrastructure
Sequence Diagram(s)sequenceDiagram
participant ClaudeCommand as Claude Command
participant PromptRenderer as Prompt Renderer
participant LLM as Local LLM
participant Intelligence as Intelligence pkg
participant SchemaValidator as Schema Validator
ClaudeCommand->>PromptRenderer: renderExtractPrompt(resume file)
PromptRenderer->>LLM: run extract prompt
LLM-->>PromptRenderer: Resume JSON
ClaudeCommand->>Intelligence: detectArchetype(resume.rawText)
ClaudeCommand->>PromptRenderer: renderScorePrompt(archetype, jd)
PromptRenderer->>LLM: run score prompt
LLM-->>SchemaValidator: EvalResult JSON
SchemaValidator-->>ClaudeCommand: validated EvalResult
ClaudeCommand->>PromptRenderer: renderValidateClaimsPrompt()
PromptRenderer->>LLM: run validate-claims
LLM-->>SchemaValidator: Claim[] validated
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related issues
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
apps/cli/README.md (1)
21-23: ⚡ Quick winAdd language specifier to fenced code blocks.
Lines 21 and 27 contain bash examples without language specification. Markdown linters and renderers recommend explicit language identifiers for syntax highlighting.
Fix markdown code block language specifiers
- ``` + ```bash git clone https://github.com/TechImmigrants/cv-builder.git cd cv-builder claude # open Claude Code at the repo root - ``` + ```And similarly on line 27:
- ``` + ```bash /evaluate-cv ./my-resume.pdf --jd ./job.md - ``` + ```🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/cli/README.md` around lines 21 - 23, Add explicit language specifiers to the two fenced code blocks in README.md that show the CLI examples: change the opening ``` for the blocks containing "/evaluate-cv ./my-resume.pdf" and "/evaluate-cv ./my-resume.pdf --jd ./job.md" to ```bash so markdown linters/renderers can apply shell syntax highlighting.Source: Linters/SAST tools
packages/intelligence/src/rubric.ts (1)
5-18: ⚡ Quick winType-level enforcement gap between
keyunion andRUBRIC_DIMENSIONS.The
RubricDimension.keyunion (lines 6-12) is manually maintained and separate from the actualRUBRIC_DIMENSIONSarray entries. If a dimension is added to the array with a new key, TypeScript won't catch the missing union member, and vice versa.Consider deriving the union from the array or adding a type assertion to enforce consistency:
♻️ Proposed fix to derive the key union from the array
-export interface RubricDimension { - key: - | "shippedEvidence" - | "quantifiedImpact" - | "toolingVisibility" - | "atsCompatibility" - | "keywordMatch" - | "publicProof"; - name: string; - description: string; - // What a 0 vs a 5 looks like, used by the score prompt as anchors. - low: string; - high: string; -} - export const RUBRIC_DIMENSIONS: RubricDimension[] = [ +export const RUBRIC_DIMENSIONS = [ { key: "shippedEvidence", name: "Shipped Evidence", description: "Real work that reached production, with named tools and outcomes.", low: "Responsibilities only; nothing shows it shipped.", high: "Every role names what was built, for whom, and the result.", }, // ... rest of dimensions -]; +] as const satisfies readonly RubricDimension[]; + +export interface RubricDimension { + key: (typeof RUBRIC_DIMENSIONS)[number]["key"]; + name: string; + description: string; + low: string; + high: string; +}Alternatively, use a
satisfiesconstraint on the array to ensure all keys match the union.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/intelligence/src/rubric.ts` around lines 5 - 18, The RubricDimension.key union is maintained separately from the RUBRIC_DIMENSIONS data, so adding/removing keys can become inconsistent; update the code to derive the key union from the actual RUBRIC_DIMENSIONS entries (or make RUBRIC_DIMENSIONS the source-of-truth) by converting RUBRIC_DIMENSIONS into a const array and inferring the key type (e.g., derive a type like typeof RUBRIC_DIMENSIONS[number]['key'] for RubricDimension['key']) or apply a satisfies/assertion to ensure RUBRIC_DIMENSIONS elements conform to the RubricDimension shape; target symbols: RubricDimension, RubricDimension.key, and RUBRIC_DIMENSIONS.packages/prompts/src/index.ts (1)
33-58: 💤 Low valueConsider validating that all placeholders were replaced.
The
renderScorePromptfunction replaces multiple placeholders (lines 50-57), but if a placeholder is misspelled in the template (e.g.,{{ARCHTYPE_NAME}}), it will silently remain in the output. While the test suite checks for absence of{{(line 24 in prompts.test.ts), runtime validation would catch template/renderer mismatches earlier.🛡️ Optional runtime validation
export function renderScorePrompt({ archetype, jdKeywords, }: ScorePromptOptions): string { const weights = Object.entries(archetype.evaluationWeights) .map(([key, value]) => `- ${key}: ${value}`) .join("\n"); const rubric = RUBRIC.dimensions .map((d) => `- **${d.name}** — ${d.description}\n - 0: ${d.low}\n - 5: ${d.high}`) .join("\n"); const keywords = jdKeywords && jdKeywords.length > 0 ? `Match against these JD keywords: ${jdKeywords.join(", ")}.` : `No JD provided — match against the role's own keyword set: ${archetype.keywords.join(", ")}.`; - return loadTemplate("score") + const rendered = loadTemplate("score") .replaceAll("{{ARCHETYPE_NAME}}", archetype.name) .replaceAll("{{ARCHETYPE_ID}}", archetype.id) .replaceAll("{{ARCHETYPE_VERSION}}", archetype.version) .replaceAll("{{RUBRIC_VERSION}}", RUBRIC_VERSION) .replace("{{WEIGHTS}}", weights) .replace("{{RUBRIC}}", rubric) .replace("{{JD_KEYWORDS}}", keywords); + + if (rendered.includes("{{")) { + throw new Error("Unreplaced placeholder in score prompt"); + } + return rendered; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/prompts/src/index.ts` around lines 33 - 58, renderScorePrompt currently performs several .replace/.replaceAll calls on the loaded "score" template but does not verify that all template placeholders were actually replaced; add a post-replacement validation in renderScorePrompt that scans the final string (e.g. for any remaining "{{...}}" tokens) and throws or logs a clear error including the template name and archetype identifiers (use ARCHETYPE_NAME/ARCHETYPE_ID/ARCHETYPE_VERSION and RUBRIC_VERSION to give context) so template/placeholder mismatches are caught at runtime; also make the replacements consistent (use replaceAll for every placeholder) before performing the validation.packages/prompts/prompts/score.md (1)
64-64: ⚡ Quick win"No code fence" instruction ambiguity in
score.mdandvalidate-claims.md.Both
packages/prompts/prompts/score.md(line 64) andpackages/prompts/prompts/validate-claims.md(line 32) include instructions that say "Output only the JSON. No prose, no code fence." However, the example schemas immediately above each instruction use code fences (```json). This creates potential confusion for the model about whether code fences are acceptable in the output.Consider rewording both instructions to be more explicit, e.g., "Return only the raw JSON [object/array] below, with no markdown formatting, no code fence (```json), and no additional text before or after."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/prompts/prompts/score.md` at line 64, The two prompt files packages/prompts/prompts/score.md and packages/prompts/prompts/validate-claims.md contain an ambiguous instruction "Output only the JSON. No prose, no code fence." conflicting with the examples that use ```json fences; update each file to a clearer instruction such as: "Return only the raw JSON object/array below, with no markdown formatting, no code fence (```json), and no additional text before or after." Locate the exact string "Output only the JSON. No prose, no code fence." in score.md (around the example schema) and validate-claims.md and replace it with the new explicit wording so the model knows to emit plain JSON without fences or extra text.packages/prompts/prompts/extract.md (2)
41-41: 💤 Low valueConsider adding explicit validation guidance to the prompt.
While the JSON-only instruction is clear, the prompt does not explicitly state whether all top-level fields are required or optional, or what the LLM should do if ambiguous/missing data is encountered. Adding an explicit instruction like "All fields must be present in the JSON object; use null or empty arrays for missing values" would reduce the chance of malformed output.
💡 Optional improvement: Add explicit validation guidance
- Output only the JSON. No prose, no code fence. + Output only the JSON object. All top-level fields must be present: + - Use null for optional string fields that are missing (e.g., "headline": null) + - Use [] for array fields that are empty + - No prose, no code fence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/prompts/prompts/extract.md` at line 41, The prompt currently says "Output only the JSON. No prose, no code fence." but lacks explicit validation rules; update the prompt in extract.md to require that all top-level fields must be present in the returned JSON and specify how to represent missing or ambiguous values (e.g., use null for missing scalar fields and empty arrays for missing lists), and instruct the model to still output a valid JSON object even when data is incomplete or ambiguous; reference the existing phrase "Output only the JSON. No prose, no code fence." as the location to append this validation guidance.
13-39: Alignextract.mdinlineResumeexample withpackages/schemas
packages/prompts/prompts/extract.md’s inline JSON matchespackages/schemas’ResumeSchemastructure:rawTextis required,links[].typeuses the same enum values, andexperience/educationfields (company/roleandinstitutionrequired; date/text fields optional) line up with the Zod schema.Optional refinement: the prompt example doesn’t reflect Zod defaults (
contact,links,experience,education,skills, andbulletscan be omitted while still validating due to.default(...)).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/prompts/prompts/extract.md` around lines 13 - 39, Update the inline JSON in extract.md to match the ResumeSchema in packages/schemas: make rawText required, ensure links[].type uses the same enum values as ResumeSchema, mark company and role (in experience) and institution (in education) as required while keeping date/text fields optional (startDate, endDate, degree, field, year optional), and reflect that contact, links, experience, education, skills, and bullets can be omitted due to the Zod .default(...) behavior so the example shows they are optional/omittable rather than always present; refer to ResumeSchema in packages/schemas to mirror exact field names and required/optional status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/intelligence/src/validators/ats.ts`:
- Line 32: The regex checks in the ATS resume validator use dot (.) which
doesn't match newlines, so multi-line tables or tab-separated columns aren't
detected; update the condition that currently uses the patterns `/\|.*\|/` and
`/\t.*\t/` to use a dotall-aware pattern (either add the `s` flag like
`/\|.*\|/s` and `/\t.*\t/s` or replace `.*` with `[\s\S]*` such as
`/\|[\s\S]*\|/` and `/\t[\s\S]*\t/`) so multi-line markdown tables and tabulated
content are correctly matched in the function containing that if-check.
- Around line 30-35: The regex checks use the original resumeText while a
lowercased version is stored in text, causing inconsistent case handling; update
the table-detection and email-detection tests to run against text (the
lowercased variable) instead of resumeText (or alternatively remove the initial
toLowerCase() assignment if case sensitivity is required), so modify the
conditions that reference resumeText (the /\|.*\|/, /\t.*\t/, and the email
regex) to reference text to ensure consistent behavior.
In `@packages/schemas/package.json`:
- Around line 20-26: The devDependency vitest is pinned to a vulnerable version
("vitest": "^3.0.0"); update the vitest entry in package.json's devDependencies
to a fixed release (e.g., "^3.0.5" or "3.0.4"), then reinstall/update the
lockfile (npm/yarn/pnpm) so the lockfile reflects the patched version and run
the test suite to ensure compatibility; look for the "vitest" token in
package.json and update that value only.
---
Nitpick comments:
In `@apps/cli/README.md`:
- Around line 21-23: Add explicit language specifiers to the two fenced code
blocks in README.md that show the CLI examples: change the opening ``` for the
blocks containing "/evaluate-cv ./my-resume.pdf" and "/evaluate-cv
./my-resume.pdf --jd ./job.md" to ```bash so markdown linters/renderers can
apply shell syntax highlighting.
In `@packages/intelligence/src/rubric.ts`:
- Around line 5-18: The RubricDimension.key union is maintained separately from
the RUBRIC_DIMENSIONS data, so adding/removing keys can become inconsistent;
update the code to derive the key union from the actual RUBRIC_DIMENSIONS
entries (or make RUBRIC_DIMENSIONS the source-of-truth) by converting
RUBRIC_DIMENSIONS into a const array and inferring the key type (e.g., derive a
type like typeof RUBRIC_DIMENSIONS[number]['key'] for RubricDimension['key']) or
apply a satisfies/assertion to ensure RUBRIC_DIMENSIONS elements conform to the
RubricDimension shape; target symbols: RubricDimension, RubricDimension.key, and
RUBRIC_DIMENSIONS.
In `@packages/prompts/prompts/extract.md`:
- Line 41: The prompt currently says "Output only the JSON. No prose, no code
fence." but lacks explicit validation rules; update the prompt in extract.md to
require that all top-level fields must be present in the returned JSON and
specify how to represent missing or ambiguous values (e.g., use null for missing
scalar fields and empty arrays for missing lists), and instruct the model to
still output a valid JSON object even when data is incomplete or ambiguous;
reference the existing phrase "Output only the JSON. No prose, no code fence."
as the location to append this validation guidance.
- Around line 13-39: Update the inline JSON in extract.md to match the
ResumeSchema in packages/schemas: make rawText required, ensure links[].type
uses the same enum values as ResumeSchema, mark company and role (in experience)
and institution (in education) as required while keeping date/text fields
optional (startDate, endDate, degree, field, year optional), and reflect that
contact, links, experience, education, skills, and bullets can be omitted due to
the Zod .default(...) behavior so the example shows they are optional/omittable
rather than always present; refer to ResumeSchema in packages/schemas to mirror
exact field names and required/optional status.
In `@packages/prompts/prompts/score.md`:
- Line 64: The two prompt files packages/prompts/prompts/score.md and
packages/prompts/prompts/validate-claims.md contain an ambiguous instruction
"Output only the JSON. No prose, no code fence." conflicting with the examples
that use ```json fences; update each file to a clearer instruction such as:
"Return only the raw JSON object/array below, with no markdown formatting, no
code fence (```json), and no additional text before or after." Locate the exact
string "Output only the JSON. No prose, no code fence." in score.md (around the
example schema) and validate-claims.md and replace it with the new explicit
wording so the model knows to emit plain JSON without fences or extra text.
In `@packages/prompts/src/index.ts`:
- Around line 33-58: renderScorePrompt currently performs several
.replace/.replaceAll calls on the loaded "score" template but does not verify
that all template placeholders were actually replaced; add a post-replacement
validation in renderScorePrompt that scans the final string (e.g. for any
remaining "{{...}}" tokens) and throws or logs a clear error including the
template name and archetype identifiers (use
ARCHETYPE_NAME/ARCHETYPE_ID/ARCHETYPE_VERSION and RUBRIC_VERSION to give
context) so template/placeholder mismatches are caught at runtime; also make the
replacements consistent (use replaceAll for every placeholder) before performing
the validation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 4b8813fd-e156-4921-964a-025af1b5c279
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (45)
.claude/commands/evaluate-cv.md.claude/commands/setup-profile.md.claude/settings.json.claude/skills/cv-evaluation/SKILL.md.claude/skills/cv-evaluation/archetypes.md.claude/skills/cv-evaluation/claim-validation.md.claude/skills/cv-evaluation/rubric.md.claude/skills/cv-evaluation/scoring.md.claude/welcome.shCLAUDE.mdapps/cli/README.mdapps/cli/package.jsonpackages/intelligence/README.mdpackages/intelligence/package.jsonpackages/intelligence/src/__tests__/intelligence.test.tspackages/intelligence/src/archetypes/data-ml-engineer.tspackages/intelligence/src/archetypes/index.tspackages/intelligence/src/archetypes/product-manager.tspackages/intelligence/src/archetypes/software-engineer.tspackages/intelligence/src/detect.tspackages/intelligence/src/index.tspackages/intelligence/src/rubric.tspackages/intelligence/src/validators/ats.tspackages/intelligence/src/validators/claims.tspackages/intelligence/tsconfig.jsonpackages/intelligence/vitest.config.tspackages/prompts/README.mdpackages/prompts/package.jsonpackages/prompts/prompts/extract.mdpackages/prompts/prompts/score.mdpackages/prompts/prompts/validate-claims.mdpackages/prompts/src/__tests__/prompts.test.tspackages/prompts/src/index.tspackages/prompts/tsconfig.jsonpackages/prompts/vitest.config.tspackages/schemas/README.mdpackages/schemas/package.jsonpackages/schemas/src/__tests__/schemas.test.tspackages/schemas/src/archetype.tspackages/schemas/src/evaluation.tspackages/schemas/src/index.tspackages/schemas/src/job-description.tspackages/schemas/src/resume.tspackages/schemas/tsconfig.jsonpackages/schemas/vitest.config.ts
…to feature/intelligence-rubric-archetypes
…etypes' into feature/prompts-pack
…le readme example
…re/cli-power-user-pack
What does this PR do?
The headline power-user surface: clone the repo, open Claude Code, run
/evaluate-cv. Everything runs locally — no server, no account, no API keys..claude/skills/cv-evaluation/— the skill that runs extract → detect →score → validate-claims. Its subfiles point at the package sources
(
packages/prompts,packages/intelligence,packages/schemas) rather thanduplicating them, so they can't drift.
.claude/commands/—/evaluate-cv(drives the skill) and.claude/settings.json+.claude/welcome.sh— aSessionStarthook thatgreets the user with the available commands on open (degrades silently without
jq).CLAUDE.mdandapps/cli/README.md— orientation for the model and ahuman quickstart.
No build needed to evaluate — the skill reads the prompts and rubric straight
from the repo.
Related issue
Closes #65
Type of change
Checklist
Note for reviewers
End-to-end
/evaluate-cvis an interactive LLM run, not an automated test —worth a manual smoke check on a sample resume. Stacked on #64 → #63 → #47.
Summary by CodeRabbit
New Features
Documentation
Tests