mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
feat(commands): add GitHub repository project extraction to /expand (#437)
This commit is contained in:
@@ -55,6 +55,7 @@ Look up the GitHub username from `01-candidate-profile.md`. If a GitHub URL or u
|
||||
2. For each repository found:
|
||||
- Fetch the repository README
|
||||
- Note: name, description, primary language(s), topics/tags, any frameworks or libraries mentioned in the README
|
||||
- If the repository represents an independent technical project (not an empty stub or uncustomized fork), extract a project summary (problem domain, tech stack, and demonstrable technical results) for consideration under Independent Projects
|
||||
3. Also retrieve the full repository list if available (to catch unpinned repos)
|
||||
|
||||
If no GitHub username or URL is found in the profile, skip this source and note it was skipped.
|
||||
@@ -108,19 +109,25 @@ After enriching all items, build a deduplicated competency map. Group findings i
|
||||
**Domain Knowledge** (subject matter expertise: geophysics, ML, NLP, etc.)
|
||||
**Methods and Practices** (agile, version control, reproducibility, testing, etc.)
|
||||
**Soft / Behavioral** (leadership, communication, collaboration signals from references and project descriptions)
|
||||
**Independent Projects & Portfolio** (distinct technical projects from GitHub with problem domain, tech stack, and key technical milestone)
|
||||
|
||||
For each competency, record:
|
||||
- The competency name
|
||||
- The source item it came from (e.g. "Coursera — Deep Learning Specialisation", "GitHub — repo-name", "Reference letter — Jens Jensen")
|
||||
- Whether it came from direct lookup (A), inference (B), or both
|
||||
|
||||
For each project, record:
|
||||
- Project name
|
||||
- One-line summary: problem tackled, tech stack used, and verifiable outcome/impact
|
||||
- Source (e.g. "GitHub — repo-name")
|
||||
|
||||
Remove anything already present in `01-candidate-profile.md` or `02-behavioral-profile.md`.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Present Grouped Summary
|
||||
|
||||
Present all new competencies for the user's review before writing anything. Format:
|
||||
Present all new competencies and project additions for the user's review before writing anything. Format:
|
||||
|
||||
```
|
||||
## /expand found [N] new competency signals across [M] sources
|
||||
@@ -131,6 +138,11 @@ Source: [Course/cert name — Provider]
|
||||
+ [Competency 2]
|
||||
...
|
||||
|
||||
**PROJECTS & PORTFOLIO**
|
||||
Source: [GitHub — repo-name]
|
||||
+ [Project Name]: [Problem, stack, and outcome]
|
||||
...
|
||||
|
||||
**GITHUB — [repo-name]**
|
||||
Source: README + inferred from tech stack
|
||||
+ [Competency 1]
|
||||
@@ -169,6 +181,7 @@ Wait for the user's response before writing anything.
|
||||
Apply only the confirmed items. Use the Edit tool to add to the relevant sections of each file — do not rewrite entire files.
|
||||
|
||||
### Additions to `01-candidate-profile.md`
|
||||
- Independent projects → append to the `## Independent Projects` section formatted as `- **[Project Name]**: [Description with stack and outcome] *(GitHub — repo-name)*`
|
||||
- Technical skills (primary and secondary) → append to the Technical Skills section
|
||||
- Domain knowledge → append to the Domain Knowledge or Technical Skills section (match the existing structure)
|
||||
- Methods and practices → append appropriately
|
||||
@@ -189,7 +202,7 @@ After writing, present:
|
||||
## /expand Complete
|
||||
|
||||
### Added to 01-candidate-profile.md
|
||||
[List each competency added, with source]
|
||||
[List each competency and independent project added, with source]
|
||||
|
||||
### Added to 02-behavioral-profile.md
|
||||
[List each behavioral signal added, with source]
|
||||
@@ -214,3 +227,4 @@ After writing, present:
|
||||
- **User confirms before writing.** The full competency map is shown and confirmed before a single file is touched.
|
||||
- **Behavioral signals are labeled.** Anything inferred from tone, language, or indirect signals is marked as inferred so it is reviewed critically.
|
||||
- **GitHub is fully scanned.** All public repositories are checked, not just pinned ones — unpinned repos often contain significant competency signals.
|
||||
- **Portfolio & projects grounded in code.** Independent projects added to the profile must reflect real projects found in public GitHub repositories — never fabricated project claims.
|
||||
|
||||
@@ -41,6 +41,12 @@ per-file diff commands.
|
||||
|
||||
### Added
|
||||
|
||||
- **`/expand` project and portfolio expansion** (`.claude/commands/expand.md`,
|
||||
`tests/test_expand_command.py`) - expands candidate discovery
|
||||
to technical projects from public GitHub repositories, extracting structured summaries
|
||||
(problem domain, tech stack, key technical challenges, and verifiable outcomes) to
|
||||
populate the `## Independent Projects` section of `01-candidate-profile.md`.
|
||||
|
||||
- **Stale sweep branch in `/outcome`** (`.claude/commands/outcome.md`,
|
||||
`tests/test_outcome_stale.py`) - introduces `/outcome stale [N]` (and `/outcome sweep [N]`)
|
||||
to batch-resolve open applications quiet for 60+ (or N) days. Displays a numbered summary
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Tests for the /expand command specification."""
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
EXPAND_COMMAND_FILE = REPO_ROOT / ".claude" / "commands" / "expand.md"
|
||||
|
||||
|
||||
class ExpandCommandTests(unittest.TestCase):
|
||||
def test_expand_command_file_exists(self):
|
||||
self.assertTrue(EXPAND_COMMAND_FILE.exists(), "expand.md must exist under .claude/commands/")
|
||||
|
||||
def test_expand_command_file_starts_with_correct_header(self):
|
||||
text = EXPAND_COMMAND_FILE.read_text(encoding="utf-8")
|
||||
first_line = text.lstrip().splitlines()[0]
|
||||
self.assertTrue(
|
||||
first_line.startswith("# /expand"),
|
||||
f"Command file must start with '# /expand', got: {first_line!r}",
|
||||
)
|
||||
|
||||
def test_expand_covers_all_discovery_sources(self):
|
||||
text = EXPAND_COMMAND_FILE.read_text(encoding="utf-8")
|
||||
sources = [
|
||||
"documents/cv/",
|
||||
"documents/linkedin/",
|
||||
"documents/diplomas/",
|
||||
"documents/references/",
|
||||
"GitHub Profile",
|
||||
]
|
||||
for src in sources:
|
||||
self.assertIn(src, text, f"expand.md must include discovery source: {src}")
|
||||
|
||||
def test_expand_maps_github_projects_to_independent_projects_section(self):
|
||||
text = EXPAND_COMMAND_FILE.read_text(encoding="utf-8")
|
||||
self.assertIn("## Independent Projects", text)
|
||||
self.assertIn("Independent Projects & Portfolio", text)
|
||||
self.assertIn("GitHub — repo-name", text)
|
||||
self.assertIn("Portfolio & projects grounded in code", text)
|
||||
self.assertNotIn("documents/projects/", text)
|
||||
|
||||
def test_expand_enforces_additive_and_confirmation_principles(self):
|
||||
text = EXPAND_COMMAND_FILE.read_text(encoding="utf-8")
|
||||
self.assertIn("Additive only", text)
|
||||
self.assertIn("User confirms before writing", text)
|
||||
self.assertIn("`all`", text)
|
||||
self.assertIn("`review`", text)
|
||||
self.assertIn("`skip`", text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user