8. cmd/update.go - parse PURL when package arg is provided:
Now let me verify the build compiles before writing tests.
>cd /Users/andrew/code/git-pkgs/git-pkgs && go build ./...Clean build. Now let me write the tests.
Now let me run the new tests first.
>go test ./cmd/ -run TestParsePackageArg -v 2>&1All unit tests pass. Now run the integration tests.
>go test ./cmd/ -run "TestWhy(AcceptsPURL)|TestHistory(AcceptsPURL)|TestWhere(AcceptsPURL)|TestAdd(AcceptsPURL)|TestRemove(AcceptsPURL)|TestUpdate(AcceptsPURL)|TestBrowse(AcceptsPURL)" -v 2>&1All passing. Now run the full test suite.
>go test ./cmd/ -v 2>&1>tail -5 /Users/andrew/.claude/projects/-Users-andrew-code-git-pkgs/8469ea1b-abe5-4a16-9071-e531cc8f005c/tool-results/toolu_01Jz5RRjbM6b1RPy6iDVK6r4.txtAll tests pass. Let me also run go vet.
>go vet ./... 2>&1Everything passes. Here's a summary of what was done:
New helper (cmd/helpers.go):
ParsePackageArg(arg, ecosystemFlag) that returns (ecosystem, name, version, error). When the arg is a PURL, it parses out the ecosystem, name, and version; when it's a plain name, it passes through the ecosystem flag.8 commands updated:
why, history, where -- call ParsePackageArg at the top, use returned ecosystem/name for database queriesvulns history -- added --ecosystem flag it was missing, call ParsePackageArg, filter the dep-matching loop by ecosystem when setbrowse, remove -- call ParsePackageArg, use returned ecosystem for FilterByEcosystem and name as the package argadd -- call ParsePackageArg, use PURL version as the version arg when no explicit version argument is given (so git-pkgs add pkg:npm/lodash@4.17.21 works like git-pkgs add lodash 4.17.21 -e npm)update -- call ParsePackageArg when a package arg is provided, use returned ecosystem for filteringTests (cmd/helpers_test.go):
TestParsePackageArg -- 9 cases covering plain names, PURLs with/without version, namespaced PURLs, ecosystem mapping, flag-ignored-when-PURL, invalid PURL errorwhy, history, where verifying PURL produces same output as --ecosystem flagopen a pr
Let me check the current state of the repo first.
>cd /Users/andrew/code/git-pkgs && git status>cd /Users/andrew/code/git-pkgs && git log --oneline -5>cd /Users/andrew/code/git-pkgs && git branch --show-current>cd /Users/andrew/code/git-pkgs && git diffThe parent git-pkgs directory isn't a git repo -- the actual repo must be in a subdirectory. Let me find it.
>git status && echo "---" && git branch --show-current && echo "---" && git log --oneline -5>git diff HEADGood. Let me also check the remote and read the README before creating the branch and PR.
>git remote -v>git log --oneline -3The user wants me to document the PURL support in the README. I need to add mentions of PURL support to the relevant command sections. Let me look at how urls documents it and follow that pattern.
Looking at the README, I need to add PURL examples to the sections for:
The vulnerability scanning section also needs PURL examples. I think the cleanest approach is to add PURL examples directly to each relevant command section rather than creating a separate subsection, since the urls command already demonstrates this pattern well. This keeps the documentation maintainable and the examples close to where users need them.
I'll add a brief PURL note early in the Quick start section to introduce the concept, then sprinkle concrete examples throughout the command sections that support it.
Now I need to add PURL documentation to the README. Let me add examples to the relevant command sections.
Now add a general note about PURL support. The urls section already explains PURLs. I'll add a note in the Configuration section about PURL arguments being accepted across commands.
Now let me make sure everything still builds and tests pass with the README changes, then create the branch, commit, and PR.
>go build ./... && go test ./cmd/ -run TestParsePackageArg -count=1 2>&1Now let me create the branch, commit, and push.
>git status>git checkout -b purl-package-args>git add README.md cmd/helpers.go cmd/helpers_test.go cmd/why.go cmd/history.go cmd/where.go cmd/vulns.go cmd/browse.go cmd/add.go cmd/remove.go cmd/update.go>git commit -m "$(cat <<'EOF'
Accept PURLs as package arguments in more commands
Add ParsePackageArg helper that parses a PURL or passes through a plain
name with the ecosystem flag. Use it in why, history, where, browse, add,
remove, update, and vulns history so users can write
`git pkgs why pkg:npm/lodash` instead of `git pkgs why lodash -e npm`.
The add command also extracts the version from the PURL, so
`git pkgs add pkg:npm/lodash@4.17.21` works like
`git pkgs add lodash 4.17.21 -e npm`.
Adds --ecosystem flag to vulns history (was missing).
Closes #108
EOF
)">git push -u origin purl-package-args 2>&1>gh pr create --title "Accept PURLs as package arguments in more commands" --body "$(cat <<'EOF'
Adds a shared ParsePackageArg helper and uses it in 8 commands so users can write `git pkgs why pkg:npm/lodash` instead of `git pkgs why lodash -e npm`.
When the argument starts with `pkg:`, the ecosystem and name are extracted from the PURL and the `--ecosystem` flag is ignored. Plain names work as before.
Commands updated: `why`, `history`, `where`, `browse`, `add`, `remove`, `update`, `vulns history`.
The `add` command also extracts the version from the PURL, so `git pkgs add pkg:npm/lodash@4.17.21` works like `git pkgs add lodash 4.17.21 -e npm`.
Also adds the missing `--ecosystem` flag to `vulns history`.
Closes #108
EOF
)" 2>&1Implement the following plan:
The urls command already accepts PURLs (pkg:cargo/serde@1.0.0) as its package argument, parsing out the ecosystem, name, and version. Other commands that take a package name argument still require plain names plus a separate --ecosystem flag. This change extends PURL support to 8 more commands so users can write git-pkgs why pkg:npm/lodash instead of git-pkgs why lodash -e npm.
Add ParsePackageArg to cmd/helpers.go:
func ParsePackageArg(arg, ecosystemFlag string) (ecosystem, name, version string, err error) { if IsPURL(arg) { p, err := purl.Parse(arg) if err != nil { return "", "", "", fmt.Errorf("parsing purl: %w", err) } ecosystem = purl.PURLTypeToEcosystem(p.Type) name = p.FullName() version = p.Version return ecosystem, name, version, nil } return ecosystemFlag, arg, "", nil}When the arg is a PURL, the ecosystem is derived from the PURL type and the --ecosystem flag is ignored. When it's a plain name, the ecosystem comes from the flag (may be empty) and version is empty.
These use ecosystem to filter database queries. The change is the same pattern: call ParsePackageArg at the top, use the returned ecosystem instead of the raw flag.
cmd/why.go (runWhy) - replace packageName := args[0] + ecosystem flag read with ParsePackageArg(args[0], ecosystem). Pass returned ecosystem and name to db.GetWhy().
cmd/history.go (runHistory) - same pattern. Pass ecosystem and name to db.GetPackageHistory().
cmd/where.go (runWhere) - same pattern. The returned ecosystem filters which manifest types to search. The name is used for the regex search.
cmd/vulns.go (runVulnsHistory, ~line 1428) - same pattern, but this command currently has no --ecosystem flag. Add one. Use the returned ecosystem to also filter the strings.EqualFold(d.Name, packageName) loop to also check ecosystem when set, avoiding false matches across ecosystems.
These use ecosystem to filter detected package managers. The change: call ParsePackageArg, use returned ecosystem for FilterByEcosystem, and use returned name as the package arg passed to the manager.
cmd/browse.go (runBrowse) - parse PURL, use name as pkg for mgr.Path(), use ecosystem for FilterByEcosystem.
cmd/add.go (runAdd) - parse PURL, use name as package and version from PURL (if present) as version arg. So git-pkgs add pkg:npm/lodash@4.17.21 works like git-pkgs add lodash 4.17.21 -e npm.
cmd/remove.go (runRemove) - parse PURL, use name as package, ecosystem for filtering.
cmd/update.go (runUpdate) - parse PURL when a package arg is provided, use name as package, ecosystem for filtering.
cmd/helpers.go - add ParsePackageArg, add purl importcmd/why.go - use ParsePackageArgcmd/history.go - use ParsePackageArgcmd/where.go - use ParsePackageArgcmd/vulns.go - add --ecosystem flag to vulns history, use ParsePackageArgcmd/browse.go - use ParsePackageArgcmd/add.go - use ParsePackageArg, use version from PURLcmd/remove.go - use ParsePackageArgcmd/update.go - use ParsePackageArgAdd to cmd/urls_test.go (or a new cmd/helpers_test.go since ParsePackageArg lives in helpers):
TestParsePackageArg - unit tests for the shared helper covering: PURL input extracts ecosystem/name/version, plain name passes through ecosystem flag, PURL with namespace, invalid PURL returns error, ecosystem flag ignored when PURL providedAdd integration tests for each command (following the urls_test.go pattern with createTestRepo/runCmd):
--ecosystem + plain namecd /Users/andrew/code/git-pkgs/git-pkgs && go build ./...go test ./cmd/ -run TestParsePackageArggo test ./cmd/ -v (full test suite)go vet ./... and golangci-lint run if availableIf you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/andrew/.claude/projects/-Users-andrew-code-git-pkgs/cadb53f4-f34f-4fcf-9066-e88933cb2fbc.jsonl
Now let me also look at the existing tests and the purl package to understand the purl.Parse, purl.PURLTypeToEcosystem, and purl.FullName() methods.
Now let me check the existing test patterns and the purl package.
I'll start by reading the key files to understand the current patterns before implementing.
Now let me read the test helper file to understand createTestRepo, runCmd, etc.
Let me read the test helpers and a few more files I need.
Now I have a good understanding of the codebase. Let me also quickly check what test fixtures exist for the test files (packageJSON, etc).
Let me check for test fixture data used across tests.
Good, I have all the context I need. Let me now implement the changes. I'll create tasks to track progress.
Now let me add the ParsePackageArg helper to cmd/helpers.go.
The import will be used once we save; the diagnostic is transient since we just added the function. Let me verify it compiles.
Now let me update all 8 commands. I'll do the database-querying ones first, then the manager-operating ones.
1. cmd/why.go - replace packageName := args[0] + ecosystem flag read with ParsePackageArg:
2. cmd/history.go - same pattern:
3. cmd/where.go - same pattern:
4. cmd/vulns.go (runVulnsHistory) - add --ecosystem flag and use ParsePackageArg:
Now update the runVulnsHistory function to use ParsePackageArg and the ecosystem flag:
Now update the loop in runVulnsHistory that matches the package name to also filter by ecosystem when set:
Good, the ecosystem variable from ParsePackageArg is now used in the loop. Wait, the diagnostic says "declared and not used" - let me check. The ecosystem variable is declared in the ParsePackageArg call on line ~1435 and used in the loop. Let me re-check the file.
The code looks correct - ecosystem is used on line 1475. The diagnostic may have been stale. Let me now update the manager-operating commands.
5. cmd/browse.go - parse PURL:
6. cmd/add.go - parse PURL, use version from PURL:
7. cmd/remove.go - parse PURL:
func ParsePackageArg(arg, ecosystemFlag string) (ecosystem, name, version string, err error) { if IsPURL(arg) { p, err := purl.Parse(arg) if err != nil { return "", "", "", fmt.Errorf("parsing purl: %w", err) } ecosystem = purl.PURLTypeToEcosystem(p.Type) name = p.FullName() version = p.Version return ecosystem, name, version, nil } return ecosystemFlag, arg, "", nil}