Skip to content

feat: add Google Antigravity workflow support #364

Open
peppervikes wants to merge 1 commit intosantifer:mainfrom
peppervikes:antigravity_version
Open

feat: add Google Antigravity workflow support #364
peppervikes wants to merge 1 commit intosantifer:mainfrom
peppervikes:antigravity_version

Conversation

@peppervikes
Copy link
Copy Markdown

@peppervikes peppervikes commented Apr 18, 2026

What does this PR do?

Related issue

Type of change

  • Bug fix
  • New feature
  • Documentation / translation
  • Refactor (no behavior change)

Checklist

  • I have read CONTRIBUTING.md
  • I linked a related issue above (required for features and architecture changes)
  • My PR does not include personal data (CV, email, real names)
  • I ran node test-all.mjs and all tests pass
  • My changes respect the Data Contract (no modifications to user-layer files)
  • My changes align with the project roadmap

Questions? Join the Discord for faster feedback.

Summary by CodeRabbit

  • New Features

    • Batch evaluation and ranking of multiple job offers
    • Side-by-side offer comparison tool
    • Automated application form answer drafting
    • Deep company research and open roles discovery
    • ATS-optimized PDF generation for job applications
    • Integrated application pipeline tracking and status management
    • Personalized outreach message generation
    • Skill gap assessment with learning plan generation
  • Documentation

    • Complete setup and installation guide
    • Comprehensive workflow reference and daily operational runbooks

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

📝 Walkthrough

Walkthrough

This pull request adds comprehensive Antigravity native workflow support to the career-ops repository, introducing agent personas, thirteen workflow definitions covering job evaluation through application tracking, setup documentation, and a DuckDuckGo search test utility. Changes total ~1,000 lines across documentation, workflow definitions, and a single npm dependency addition.

Changes

Cohort / File(s) Summary
Agent & Workflow Definitions
.agents/agents.md, .agents/workflows/career-ops*.md
Added agent personas (evaluator, tailor, scanner, storywriter, tracker) with explicit goals and constraints, plus thirteen workflow definitions covering evaluation, batch processing, PDF generation, scanning, tracking, pipeline management, outreach drafting, research, comparison, application answering, training plan generation, project planning, and main routing interface.
Setup & Operational Documentation
career-ops-starter-guide.md, docs/ANTIGRAVITY.md
Added comprehensive Antigravity integration guides: starter guide with setup steps, file/command reference tables, and operational runbooks; ANTIGRAVITY.md with prerequisites, one-time setup sequence, customization locations, troubleshooting, and command/workflow mapping details.
Dependencies & Utilities
package.json, test_ddg.js
Added pdf-parse npm dependency for PDF processing; introduced DuckDuckGo site-restricted search utility script with URL/title extraction and normalization via regex matching.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: add Google Antigravity workflow support' accurately summarizes the main objective of the changeset, which adds comprehensive Google Antigravity workflow definitions across multiple files including agents, workflows, documentation, and dependencies.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to career-ops, @peppervikes! Thanks for your first PR.

A few things to know:

  • Tests will run automatically — check the status below
  • Make sure you've linked a related issue (required for features)
  • Read CONTRIBUTING.md if you haven't

We'll review your PR soon. Join our Discord if you have questions.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/agents.md:
- Line 23: In the "Goal" line where the output format is described as
"markdown", update the wording to capitalize the markup language name as
"Markdown" for consistency; locate the phrase "**Goal**: Produce a job-specific
CV in markdown with relevant keywords surfaced, truthfully — never fabricate
experience." and change "markdown" to "Markdown".

In @.agents/workflows/career-ops-batch.md:
- Line 17: The workflow docs say to use the Antigravity Agent Manager but the
implementation actually uses claude -p workers; pick one consistent approach and
update files accordingly: either (A) implement the Agent Manager flow by
replacing the claude -p orchestration in "modes/batch.md" with logic to spawn N
sub-agents (one per JD), load personas from ".agents/agents.md", run the
"modes/oferta.md" persona flow per sub-agent and aggregate results into a ranked
table (also update "batch/batch-prompt.md" to remove self-contained worker
prompts and use persona-driven prompts), or (B) keep the current claude -p
worker implementation and update ".agents/workflows/career-ops-batch.md" to
document the claude -p approach and remove Agent Manager wording; ensure all
three files ("career-ops-batch.md", "modes/batch.md", "batch/batch-prompt.md")
are edited so documentation and implementation match and reference the selected
orchestration style.

In @.agents/workflows/career-ops-contact.md:
- Line 18: The workflow text in career-ops-contact.md currently requires "under
80 words" which conflicts with modes/contacto.md that enforces "Maximo 300
caracteres (LinkedIn connection request limit)"; update career-ops-contact.md to
reference the 300-character LinkedIn limit (e.g., "Max 300 characters") instead
of an 80-word limit so both documents align and reflect the actual LinkedIn
constraint.

In @.agents/workflows/career-ops-deep.md:
- Line 16: The documentation line in modes/deep.md claiming "Pull all open roles
on their careers page (Playwright)" is misleading because modes/deep.md only
builds a structured research prompt; update modes/deep.md to accurately describe
its behavior (e.g., "Generate structured research prompts for external tools
(Perplexity/Claude/ChatGPT)") or, if deep mode should perform browser scraping,
integrate the Playwright scraping logic from modes/scan.md into deep mode by
reusing the scraping functions and wiring them into the deep workflow; locate
the phrase "Pull all open roles on their careers page (Playwright)" in
modes/deep.md and either edit the text to the correct description or add the
Playwright invocation and helper functions copied/adapted from modes/scan.md so
the implementation matches the doc.

In @.agents/workflows/career-ops-evaluate.md:
- Line 18: Update the inaccurate block count in the string "6-block evaluation
(A-F + Block G Legitimacy + Global score)" so it correctly reflects seven
blocks; replace it with either "7-block evaluation (A-G + Global score)" or
"full evaluation (blocks A-F + Block G Legitimacy + Global score)". Locate and
edit that exact phrase in .agents/workflows/career-ops-evaluate.md (search for
"6-block evaluation" or "A-F + Block G Legitimacy") and update the wording to
one of the suggested corrected options to ensure mathematical accuracy.

In @.agents/workflows/career-ops-pipeline.md:
- Line 13: The workflow step "Read `modes/pipeline.md` and execute it."
incorrectly invokes the full URL inbox auto-pipeline; update the integrity-check
workflow so it no longer executes `modes/pipeline.md` — either remove that
execution step entirely or point it to a dedicated integrity-mode doc (e.g.,
`modes/integrity.md` or `modes/integrity-pipeline.md`) that implements only
dedup/normalize/merge/liveness/verify, and ensure the step text and any
references are updated accordingly to reflect the dedicated integrity mode.

In @.agents/workflows/career-ops-tracker.md:
- Line 19: Replace the vague “write back to `data/applications.md`” instruction
with an explicit rule: NEW entries must be written as TSV files into
`batch/tracker-additions/` and then merged via `merge-tracker.mjs` (never add
new rows directly to `data/applications.md`); direct edits to
`data/applications.md` are only permitted to UPDATE status or notes of existing
rows while preserving column order and the integrity rules from
`DATA_CONTRACT.md`.
- Line 7: Define an explicit mapping from the filter aliases listed in
`$ARGUMENTS` to the canonical statuses from templates/states.yml (Evaluated,
Applied, Responded, Interview, Offer, Rejected, Discarded, SKIP) and apply that
mapping before any persistence/update of the tracker status; ensure aliases like
"ghosted" → "Responded", "interviewing" → "Interview", "offer" → "Offer",
"stale" → "Discarded" (or other agreed mappings) are implemented, strip any
markdown, dates or extra text from the incoming status, validate the resolved
value against the canonical set, and reject or normalize any non-canonical
values so saves always use only the canonical status names.

In @.agents/workflows/career-ops.md:
- Line 19: The sentence "These four files" in career-ops.md is inconsistent with
the list of five files on lines 13–17; update the text so the count matches the
list (either change "These four files" to "These five files" or remove one file
from the list), and ensure the phrase "These four files" (or its replacement) is
updated wherever it appears in the document to keep the source-of-truth count
accurate for mode authors.
- Around line 49-75: The fenced code block in the career-ops help text is
missing a language tag (MD040); update the block opener to include a language
such as "text" or "md" (e.g., change ``` to ```text) so the block is properly
annotated — locate the triple-backtick that opens the career-ops listing in the
.agents/workflows/career-ops.md content and add the language tag.
- Around line 28-43: The routing rule that checks "contains `http://`,
`https://`, or JD keywords" is declared before explicit "starts with" subcommand
rules in career-ops.md, causing inputs like "scan https://..." to match the
generic auto-pipeline fallback; reorder the rules so all explicit "starts with
`scan`/`deep`/`pdf`/`oferta`/`ofertas`/`apply`/`batch`/`tracker`/`pipeline`/`contacto`/`training`/`project`/`patterns`/`followup`/`interview-prep`"
checks appear before the broad "contains URL/JD keywords" rule, ensuring
subcommands are matched first and the heuristic only applied as a final
fallback.

In `@career-ops-starter-guide.md`:
- Line 68: Update the setup instruction in career-ops-starter-guide.md to
correctly describe the artifacts: change the phrase “two folders” to “one folder
and one file” (referencing the .agents/ folder and the docs/ANTIGRAVITY.md file)
so the line reads something like “Copy the following into the repo root: the
.agents/ folder and the docs/ANTIGRAVITY.md file.”
- Around line 11-15: The document contains a machine-specific absolute path
"C:\Users\vikalp.agrawal\Desktop\career-ops\" which exposes a personal
identifier and is not portable; replace that exact string with a generic
placeholder (for Windows use something like
"C:\Users\<username>\Desktop\career-ops\" or a relative path like
"./career-ops/") or a cross-platform example (e.g. "/home/<user>/career-ops/")
so the text no longer contains a real username or absolute machine-specific
path.
- Around line 13-260: Multiple fenced code blocks in career-ops-starter-guide.md
lack language identifiers (MD040); update each triple-backtick block (e.g., the
Windows path block, the git clone/install block, the repo tree block, and the
cv.md example) to include appropriate languages like bash, text, and markdown so
linting passes and readability improves, then re-run your markdown linter
(markdownlint/MD040) to confirm all code fences now have language tags.

In `@docs/ANTIGRAVITY.md`:
- Line 10: The docs currently state "Node.js 20+" but the project's validator
script doctor.mjs enforces Node.js >=18 (and Playwright 1.58.1 requires 18), so
update the ANTIGRAVITY.md text to reflect the actual minimum requirement
(Node.js 18+) and optionally note that Node.js 20+ is recommended rather than
required; reference doctor.mjs as the authoritative validator and mention
Playwright 1.58.1 in the note to clarify compatibility.

In `@package.json`:
- Line 35: Remove the unused runtime dependency "pdf-parse" from package.json's
dependencies block; locate the "pdf-parse": "^1.1.1" entry and delete it, then
run your package manager (npm/yarn/pnpm) to update the lockfile (e.g., npm
install or yarn install) so the lockfile no longer contains the package and the
dependency tree is consistent. Ensure there are no imports of "pdf-parse" in the
repo (search for "pdf-parse") before committing.

In `@test_ddg.js`:
- Around line 11-15: The code is storing DuckDuckGo redirect wrappers instead of
the real target URL; update the finalUrl normalization (the block that currently
checks finalUrl for '?q=' and '//' — reference variable finalUrl) to also detect
DuckDuckGo redirect paths containing '/l/?uddg=' (or a uddg= query param),
extract the uddg value, URL-decode it (decodeURIComponent) and assign that
decoded target back to finalUrl before other checks so the stored URL is the
real destination; keep existing handling for '?q=' and protocol-relative '//'
as-is and ensure you handle presence of additional query params after uddg.
- Around line 1-4: The fetch call using url and the promise chain (the .then(res
=> res.text()) path) does not check res.ok, lacks timeout protection, and its
.catch only logs errors so the script can exit successfully on failures or empty
results; update the fetch usage to (1) validate the response status (check
res.ok and treat non-2xx/202/blocking HTML as an error), (2) enforce a request
timeout (abort the fetch with AbortController after a configurable timeout), (3)
detect empty or no-result pages after parsing the HTML and treat them as
failures, and (4) ensure any error path (including non-ok status, timeout,
blocked-response detection, or empty results) logs a clear message and calls
process.exit(1) so the script fails appropriately.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b5544e1f-65a1-43d3-a704-9873fde6f97f

📥 Commits

Reviewing files that changed from the base of the PR and between 10c496c and cbb7173.

📒 Files selected for processing (18)
  • .agents/agents.md
  • .agents/workflows/career-ops-apply.md
  • .agents/workflows/career-ops-batch.md
  • .agents/workflows/career-ops-compare.md
  • .agents/workflows/career-ops-contact.md
  • .agents/workflows/career-ops-deep.md
  • .agents/workflows/career-ops-evaluate.md
  • .agents/workflows/career-ops-pdf.md
  • .agents/workflows/career-ops-pipeline.md
  • .agents/workflows/career-ops-project.md
  • .agents/workflows/career-ops-scan.md
  • .agents/workflows/career-ops-tracker.md
  • .agents/workflows/career-ops-training.md
  • .agents/workflows/career-ops.md
  • career-ops-starter-guide.md
  • docs/ANTIGRAVITY.md
  • package.json
  • test_ddg.js

Comment thread .agents/agents.md

You tailor the user's master CV for a specific job description, keeping it ATS-friendly.

**Goal**: Produce a job-specific CV in markdown with relevant keywords surfaced, truthfully — never fabricate experience.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider capitalizing "Markdown" for consistency.

The term appears as "markdown" but is typically capitalized as "Markdown" when referring to the markup language (proper noun). This is a minor style preference for consistency with common usage.

✨ Proposed style fix
-**Goal**: Produce a job-specific CV in markdown with relevant keywords surfaced, truthfully — never fabricate experience.
+**Goal**: Produce a job-specific CV in Markdown with relevant keywords surfaced, truthfully — never fabricate experience.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**Goal**: Produce a job-specific CV in markdown with relevant keywords surfaced, truthfully — never fabricate experience.
**Goal**: Produce a job-specific CV in Markdown with relevant keywords surfaced, truthfully — never fabricate experience.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~23-~23: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ... Goal: Produce a job-specific CV in markdown with relevant keywords surfaced, truthf...

(MARKDOWN_NNP)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/agents.md at line 23, In the "Goal" line where the output format is
described as "markdown", update the wording to capitalize the markup language
name as "Markdown" for consistency; locate the phrase "**Goal**: Produce a
job-specific CV in markdown with relevant keywords surfaced, truthfully — never
fabricate experience." and change "markdown" to "Markdown".


Read `modes/batch.md` and execute it.

**Antigravity note:** The original Claude Code batch uses `claude -p` workers. In Antigravity, parallelize via the Agent Manager — spawn N sub-agents (one per JD) using the personas from `.agents/agents.md`, each running `modes/oferta.md` on its assigned JD. Aggregate results into a single ranked table once all return.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check which batch orchestration system is used

echo "=== Checking modes/batch.md for Agent Manager references ==="
rg -n 'Agent Manager|agent_manager|sub-agent|subagent' modes/batch.md

echo -e "\n=== Checking for claude -p worker implementation ==="
rg -n 'claude -p|claude-p|worker' modes/batch.md batch/batch-prompt.md | head -20

Repository: santifer/career-ops

Length of output: 1101


Resolve architectural mismatch between workflow documentation and implementation.

The workflow file documents using "Antigravity Agent Manager" with personas from .agents/agents.md, but the actual implementation in modes/batch.md uses claude -p workers with self-contained prompts. Specifically:

  • .agents/workflows/career-ops-batch.md (line 17): Documents Agent Manager spawning N sub-agents per JD
  • modes/batch.md (lines 14, 17, 22, 47, 85-87): Implements claude -p workers, not the Agent Manager system
  • batch/batch-prompt.md: Uses self-contained worker prompts

Choose one path:

  1. Implement the documented Agent Manager approach in modes/batch.md, or
  2. Update the workflow file to reflect the actual claude -p worker implementation
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/workflows/career-ops-batch.md at line 17, The workflow docs say to
use the Antigravity Agent Manager but the implementation actually uses claude -p
workers; pick one consistent approach and update files accordingly: either (A)
implement the Agent Manager flow by replacing the claude -p orchestration in
"modes/batch.md" with logic to spawn N sub-agents (one per JD), load personas
from ".agents/agents.md", run the "modes/oferta.md" persona flow per sub-agent
and aggregate results into a ranked table (also update "batch/batch-prompt.md"
to remove self-contained worker prompts and use persona-driven prompts), or (B)
keep the current claude -p worker implementation and update
".agents/workflows/career-ops-batch.md" to document the claude -p approach and
remove Agent Manager wording; ensure all three files ("career-ops-batch.md",
"modes/batch.md", "batch/batch-prompt.md") are edited so documentation and
implementation match and reference the selected orchestration style.

Read `modes/contacto.md` and execute it. The mode will:
- Draft a LinkedIn-style intro or email (short, specific, proof-point-first)
- Surface one concrete thing about the recipient (recent post, product, shared connection, talk) to anchor the hook
- Keep under 80 words; no "I hope this finds you well"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reconcile the message length constraint.

The workflow specifies "under 80 words" but modes/contacto.md (line 48) enforces "Maximo 300 caracteres (LinkedIn connection request limit)." LinkedIn's actual limit is 300 characters. Approximately 80 words would exceed this limit in most cases. Update the workflow to reference the character limit instead of word count for accuracy.

📝 Proposed fix to align with LinkedIn's character limit
-- Keep under 80 words; no "I hope this finds you well"
+- Keep under 300 characters (LinkedIn limit); no "I hope this finds you well"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Keep under 80 words; no "I hope this finds you well"
- Keep under 300 characters (LinkedIn limit); no "I hope this finds you well"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/workflows/career-ops-contact.md at line 18, The workflow text in
career-ops-contact.md currently requires "under 80 words" which conflicts with
modes/contacto.md that enforces "Maximo 300 caracteres (LinkedIn connection
request limit)"; update career-ops-contact.md to reference the 300-character
LinkedIn limit (e.g., "Max 300 characters") instead of an 80-word limit so both
documents align and reflect the actual LinkedIn constraint.


Read `modes/deep.md` and execute it. The mode will:
- Research the company (mission, funding, team size, recent news, layoff signals, public product)
- Pull all open roles on their careers page (Playwright)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for Playwright usage in modes/deep.md

rg -n 'playwright|browser_navigate|browser_snapshot|careers.*page' modes/deep.md

Repository: santifer/career-ops

Length of output: 45


🏁 Script executed:

# View the workflow file around line 16
head -20 .agents/workflows/career-ops-deep.md | tail -10

Repository: santifer/career-ops

Length of output: 487


🏁 Script executed:

# View the modes/deep.md file to understand its actual implementation
cat -n modes/deep.md | head -50

Repository: santifer/career-ops

Length of output: 1944


🏁 Script executed:

# Search for Playwright usage across the entire repository
rg -r 'browser_navigate|browser_snapshot|Playwright|playwright' --type markdown --type mjs --type js

Repository: santifer/career-ops

Length of output: 459


🏁 Script executed:

# Check what files exist in modes/ directory
ls -la modes/

Repository: santifer/career-ops

Length of output: 1578


🏁 Script executed:

# Search for Playwright and browser-related keywords across modes/
rg 'browser_navigate|browser_snapshot|Playwright|playwright' modes/

Repository: santifer/career-ops

Length of output: 9209


🏁 Script executed:

# Also check scan.md since it might handle scraping
cat -n modes/scan.md | head -80

Repository: santifer/career-ops

Length of output: 4964


Remove misleading Playwright claim from workflow documentation.

The workflow states that modes/deep.md will "Pull all open roles on their careers page (Playwright)", but the implementation (lines 1-47) only generates a structured research prompt for external tools (Perplexity/Claude/ChatGPT). There is no Playwright browser automation in modes/deep.md.

The actual Playwright implementation for scraping job listings exists in modes/scan.md. Either correct the workflow documentation to reflect what modes/deep.md actually does, or integrate Playwright scraping into the deep mode if it should support this capability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/workflows/career-ops-deep.md at line 16, The documentation line in
modes/deep.md claiming "Pull all open roles on their careers page (Playwright)"
is misleading because modes/deep.md only builds a structured research prompt;
update modes/deep.md to accurately describe its behavior (e.g., "Generate
structured research prompts for external tools (Perplexity/Claude/ChatGPT)") or,
if deep mode should perform browser scraping, integrate the Playwright scraping
logic from modes/scan.md into deep mode by reusing the scraping functions and
wiring them into the deep workflow; locate the phrase "Pull all open roles on
their careers page (Playwright)" in modes/deep.md and either edit the text to
the correct description or add the Playwright invocation and helper functions
copied/adapted from modes/scan.md so the implementation matches the doc.


Then read `modes/oferta.md` and execute it against `$ARGUMENTS`.

Output the full 6-block evaluation (A-F + Block G Legitimacy + Global score). Do **not** auto-generate the PDF or update the tracker — this command is evaluation only. If user wants PDF + tracker, suggest `/career-ops <JD>` (the full auto-pipeline) instead.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Correct the block count.

The description mentions "6-block evaluation (A-F + Block G Legitimacy + Global score)" but A through F represents 6 blocks, and adding Block G makes it 7 blocks total. Update to either "7-block evaluation (A-G)" or "full evaluation (blocks A-F + Block G Legitimacy)" for mathematical accuracy.

📝 Proposed fix for block count
-Output the full 6-block evaluation (A-F + Block G Legitimacy + Global score). Do **not** auto-generate the PDF or update the tracker — this command is evaluation only. If user wants PDF + tracker, suggest `/career-ops <JD>` (the full auto-pipeline) instead.
+Output the full 7-block evaluation (A-G: blocks A-F + Block G Legitimacy + Global score). Do **not** auto-generate the PDF or update the tracker — this command is evaluation only. If user wants PDF + tracker, suggest `/career-ops <JD>` (the full auto-pipeline) instead.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Output the full 6-block evaluation (A-F + Block G Legitimacy + Global score). Do **not** auto-generate the PDF or update the tracker — this command is evaluation only. If user wants PDF + tracker, suggest `/career-ops <JD>` (the full auto-pipeline) instead.
Output the full 7-block evaluation (A-G: blocks A-F + Block G Legitimacy + Global score). Do **not** auto-generate the PDF or update the tracker — this command is evaluation only. If user wants PDF + tracker, suggest `/career-ops <JD>` (the full auto-pipeline) instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/workflows/career-ops-evaluate.md at line 18, Update the inaccurate
block count in the string "6-block evaluation (A-F + Block G Legitimacy + Global
score)" so it correctly reflects seven blocks; replace it with either "7-block
evaluation (A-G + Global score)" or "full evaluation (blocks A-F + Block G
Legitimacy + Global score)". Locate and edit that exact phrase in
.agents/workflows/career-ops-evaluate.md (search for "6-block evaluation" or
"A-F + Block G Legitimacy") and update the wording to one of the suggested
corrected options to ensure mathematical accuracy.


### Step 2 — Drop in the Antigravity adapter

Copy the two folders described in Part 1 (`.agents/` and `docs/ANTIGRAVITY.md`) into the repo root.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix incorrect artifact type in setup instruction.

This says “two folders,” but docs/ANTIGRAVITY.md is a file. Please update wording (e.g., “one folder and one file”).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@career-ops-starter-guide.md` at line 68, Update the setup instruction in
career-ops-starter-guide.md to correctly describe the artifacts: change the
phrase “two folders” to “one folder and one file” (referencing the .agents/
folder and the docs/ANTIGRAVITY.md file) so the line reads something like “Copy
the following into the repo root: the .agents/ folder and the
docs/ANTIGRAVITY.md file.”

Comment thread docs/ANTIGRAVITY.md
## Prerequisites

- [Google Antigravity](https://antigravity.google) installed
- Node.js 20+ and Git (same as for the Claude Code flow)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify documented vs enforced Node version requirements.
# Expected: same minimum version everywhere.

rg -n 'Node\.js\s*[>=]*\s*[0-9]+' docs/ANTIGRAVITY.md career-ops-starter-guide.md README.md 2>/dev/null
rg -n 'checkNodeVersion|>=\s*18|>=\s*20' doctor.mjs

Repository: santifer/career-ops

Length of output: 466


🏁 Script executed:

cat package.json | grep -A 5 '"engines"'

Repository: santifer/career-ops

Length of output: 45


🏁 Script executed:

rg 'node\s*[0-9]+\.' --type json

Repository: santifer/career-ops

Length of output: 45


🏁 Script executed:

# Search for Node 20+ specific features (e.g., native fetch without flags, WebStream, etc.)
rg 'fetch\(' doctor.mjs | head -5
rg 'error\.cause' 
rg 'Array\.from.*{.*length'

Repository: santifer/career-ops

Length of output: 181


🏁 Script executed:

# Check if Playwright or other dependencies require Node 20+
cat package.json | grep -E '"playwright"|"node"|"engines"' -A 2

Repository: santifer/career-ops

Length of output: 96


🏁 Script executed:

cat package.json

Repository: santifer/career-ops

Length of output: 1146


🌐 Web query:

Playwright 1.58.1 Node.js version requirements compatibility

💡 Result:

Playwright 1.58.1 requires Node.js version >=18. The official system requirements specify the latest 20.x, 22.x or 24.x versions. Playwright docker images use Node.js v24 LTS (switched from v22 in version 1.58). Node.js 16 support was removed in version 1.55, and Node.js 18 was deprecated then but remains the minimum as per package engines in 1.58.x releases.

Citations:


Align Node.js documentation requirement with actual minimum.

This doc specifies Node.js 20+, but the project's validator (doctor.mjs) enforces >=18, which is also Playwright 1.58.1's actual minimum. Update docs to match the validator or clarify that 20+ is recommended (not required) to avoid contradictory setup guidance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ANTIGRAVITY.md` at line 10, The docs currently state "Node.js 20+" but
the project's validator script doctor.mjs enforces Node.js >=18 (and Playwright
1.58.1 requires 18), so update the ANTIGRAVITY.md text to reflect the actual
minimum requirement (Node.js 18+) and optionally note that Node.js 20+ is
recommended rather than required; reference doctor.mjs as the authoritative
validator and mention Playwright 1.58.1 in the note to clarify compatibility.

Comment thread package.json
"license": "MIT",
"dependencies": {
"js-yaml": "^4.1.1",
"pdf-parse": "^1.1.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether pdf-parse is referenced anywhere in repo code/docs.
# Expected: at least one import/require/use site if dependency is justified.

rg -n --hidden --glob '!.git' '\bpdf-parse\b|from\s+["'\'']pdf-parse["'\'']|require\(["'\'']pdf-parse["'\'']\)'

Repository: santifer/career-ops

Length of output: 105


Remove the unused pdf-parse runtime dependency.

The codebase does not import or use pdf-parse anywhere. PDF generation relies exclusively on Playwright's page.pdf() method. Keeping unused dependencies increases supply-chain attack surface and maintenance overhead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 35, Remove the unused runtime dependency "pdf-parse"
from package.json's dependencies block; locate the "pdf-parse": "^1.1.1" entry
and delete it, then run your package manager (npm/yarn/pnpm) to update the
lockfile (e.g., npm install or yarn install) so the lockfile no longer contains
the package and the dependency tree is consistent. Ensure there are no imports
of "pdf-parse" in the repo (search for "pdf-parse") before committing.

Comment thread test_ddg.js
Comment on lines +1 to +4
const url = 'https://html.duckduckgo.com/html/?q=site:instahyre.com/job+software+engineer';
fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } })
.then(res => res.text())
.then(html => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp_file="$(mktemp)"
curl -sSL \
  -A 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' \
  -o "$tmp_file" \
  -w 'HTTP status: %{http_code}\n' \
  'https://html.duckduckgo.com/html/?q=site:instahyre.com/job+software+engineer'

rg -n 'Unfortunately, bots use DuckDuckGo too|complete the following challenge|result__title|result__url' "$tmp_file" || true
rm -f "$tmp_file"

Repository: santifer/career-ops

Length of output: 155


🏁 Script executed:

cat test_ddg.js

Repository: santifer/career-ops

Length of output: 1062


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp_file="$(mktemp)"
curl -sSL \
  -A 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' \
  -o "$tmp_file" \
  -w 'HTTP status: %{http_code}\n' \
  'https://html.duckduckgo.com/html/?q=site:instahyre.com/job+software+engineer'

rg -n 'no-results|result__title|result__url' "$tmp_file" || true
rm -f "$tmp_file"

Repository: santifer/career-ops

Length of output: 79


Fail the script when DuckDuckGo blocks the request or returns no results.

fetch resolves without checking res.ok, and the .catch() handler only logs errors without setting a failing exit code. The script can exit successfully with empty results or partial failures. Additionally, the DuckDuckGo endpoint exhibits inconsistent behavior (HTTP 200, 202, or blocking responses), so the script needs defensive error handling, timeout protection, and explicit empty-result detection.

🐛 Proposed fix
 const url = 'https://html.duckduckgo.com/html/?q=site:instahyre.com/job+software+engineer';
+const controller = new AbortController();
+const timeout = setTimeout(() => controller.abort(), 10_000);
+
-fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } })
-  .then(res => res.text())
+fetch(url, {
+  headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' },
+  signal: controller.signal,
+})
+  .then(res => {
+    if (!res.ok) {
+      throw new Error(`DuckDuckGo returned HTTP ${res.status}`);
+    }
+    return res.text();
+  })
   .then(html => {
+    if (/no-results|Unfortunately|challenge/i.test(html)) {
+      throw new Error('DuckDuckGo blocked the request or returned no results');
+    }
+
     const urls = [];
     const urlRegex = /class="result__url" href="([^"]+)"/g;
     let match;
@@ -24,7 +37,12 @@ fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64
     const titleRegex = /class="result__title"[^>]*>([\s\S]*?)<\/h2>/g;
     while ((match = titleRegex.exec(html)) !== null) {
       titles.push(match[1].replace(/<[^>]+>/g, '').trim());
     }
+
+    if (urls.length === 0 || titles.length === 0) {
+      throw new Error('No DuckDuckGo results were parsed');
+    }
 
     console.log('URLs:', urls);
     console.log('Titles:', titles);
   })
-  .catch(console.error);
+  .finally(() => clearTimeout(timeout))
+  .catch(error => {
+    console.error(error);
+    process.exitCode = 1;
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_ddg.js` around lines 1 - 4, The fetch call using url and the promise
chain (the .then(res => res.text()) path) does not check res.ok, lacks timeout
protection, and its .catch only logs errors so the script can exit successfully
on failures or empty results; update the fetch usage to (1) validate the
response status (check res.ok and treat non-2xx/202/blocking HTML as an error),
(2) enforce a request timeout (abort the fetch with AbortController after a
configurable timeout), (3) detect empty or no-result pages after parsing the
HTML and treat them as failures, and (4) ensure any error path (including non-ok
status, timeout, blocked-response detection, or empty results) logs a clear
message and calls process.exit(1) so the script fails appropriately.

Comment thread test_ddg.js
Comment on lines +11 to +15
if (finalUrl.includes('?q=')) {
finalUrl = decodeURIComponent(finalUrl.split('?q=')[1].split('&')[0]);
} else if (finalUrl.startsWith('//')) {
finalUrl = 'https:' + finalUrl;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

node <<'NODE'
const href = '//duckduckgo.com/l/?kh=-1&uddg=https%3A%2F%2Fexample.com%2Fjobs&rut=abc';

let finalUrl = href;
if (finalUrl.includes('?q=')) {
  finalUrl = decodeURIComponent(finalUrl.split('?q=')[1].split('&')[0]);
} else if (finalUrl.startsWith('//')) {
  finalUrl = 'https:' + finalUrl;
}

console.log('current:', finalUrl);
console.log('expected:', 'https://example.com/jobs');
NODE

Repository: santifer/career-ops

Length of output: 184


🏁 Script executed:

fd -type f -name "test_ddg.js" -o -name "*ddg*test*"

Repository: santifer/career-ops

Length of output: 233


🏁 Script executed:

fd -type file -name "*ddg*"

Repository: santifer/career-ops

Length of output: 233


🏁 Script executed:

fd "test_ddg.js"

Repository: santifer/career-ops

Length of output: 74


🏁 Script executed:

cat -n test_ddg.js | sed -n '1,30p'

Repository: santifer/career-ops

Length of output: 1258


Decode DuckDuckGo uddg redirects before storing URLs.

DuckDuckGo HTML result links commonly use /l/?uddg=<target>. The current code preserves the DuckDuckGo redirect wrapper instead of extracting the actual result URL. The test demonstrates this: for a URL like //duckduckgo.com/l/?kh=-1&uddg=https%3A%2F%2Fexample.com%2Fjobs&rut=abc, the code stores the full wrapper instead of https://example.com/jobs.

Proposed fix
-      if (finalUrl.includes('?q=')) {
-        finalUrl = decodeURIComponent(finalUrl.split('?q=')[1].split('&')[0]);
-      } else if (finalUrl.startsWith('//')) {
-        finalUrl = 'https:' + finalUrl;
-      }
+      const parsedUrl = new URL(finalUrl.replaceAll('&amp;', '&'), 'https://duckduckgo.com');
+      const isDuckDuckGoRedirect =
+        parsedUrl.hostname.endsWith('duckduckgo.com') && parsedUrl.pathname.startsWith('/l/');
+      const redirectedUrl = isDuckDuckGoRedirect
+        ? parsedUrl.searchParams.get('uddg') ?? parsedUrl.searchParams.get('q')
+        : null;
+      finalUrl = redirectedUrl ?? parsedUrl.href;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_ddg.js` around lines 11 - 15, The code is storing DuckDuckGo redirect
wrappers instead of the real target URL; update the finalUrl normalization (the
block that currently checks finalUrl for '?q=' and '//' — reference variable
finalUrl) to also detect DuckDuckGo redirect paths containing '/l/?uddg=' (or a
uddg= query param), extract the uddg value, URL-decode it (decodeURIComponent)
and assign that decoded target back to finalUrl before other checks so the
stored URL is the real destination; keep existing handling for '?q=' and
protocol-relative '//' as-is and ensure you handle presence of additional query
params after uddg.

@santifer
Copy link
Copy Markdown
Owner

Hey @peppervikes, thanks for opening this. I want to merge Google Antigravity support — it fits the multi-CLI direction in umbrella #272 — but there are a few things I need to ask you to adjust before we can:

Things to remove:

  • test_ddg.js at repo root — this looks like experimental DuckDuckGo scraping code, not production. If you need it for development, keep it local.
  • pdf-parse dependency in package.json — adding new dependencies needs prior discussion. What do you need it for? If it's optional, make it dynamic.

Things to add:

  • Link the issue this PR addresses in the description (open one first if none exists — per CONTRIBUTING.md, feature PRs need an issue for scope alignment)
  • Fill out the checklist in the PR template (you left all boxes unchecked)

Naming collision to resolve:

  • You're using .agents/ directory, but PR feat: add Codex support via .agents/ skill files and runner #295 also claims .agents/ for Codex support. We need to decide one namespace. I'd suggest .antigravity/ or .google-antigravity/ to be explicit, following the .claude/, .qwen/, .gemini/, .opencode/ pattern (one dir per CLI, not a shared .agents/).

Once those are addressed, this has a clear path to merge under umbrella #272. Looking forward to the rework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants