feat: full Linux support for unbound discover and onboard#128
feat: full Linux support for unbound discover and onboard#128AakashVelusamy wants to merge 3 commits into
Conversation
|
@copilot resolve the merge conflicts in this pull request |
|
@greptile review my pr |
All Greptile P1 issues addressedResolved all 4 remaining issues from the latest Greptile review (commit P1:
P1:
P1:
P1:
All 504 tests pass. |
|
@greptile review the pr |
|
@greptile review the pr |
Adds Linux implementations for every AI coding tool detector and extractor, bringing Linux to full parity with macOS and Windows (detectors, rules, MCP config, settings, skills), plus cross-platform --set-cron support for user-level onboard/discover. Highlights: - linux/ package mirroring macos/windows for all 17 tools, wired through coding_tool_factory and ai_tools_discovery (multi-user /home + /root, Docker/CI root-only containers, /etc/machine-id device id). - linux_extraction_helpers with Linux-aware system-path skipping and a walk_for_tool_directories that does not blocklist /home. - Claude Cowork (~/.config/Claude) and Junie (~/.junie) Linux parity. - Plugin provenance (plugin_lookup) threaded through Linux Claude/Cursor skills and Claude MCP extractors. - setup-scheduled-scan cron support hardened across Linux/macOS/Windows. - tests/test_cowork_skills_extraction_linux.py. Rebased cleanly onto staging (which already contains plugin provenance #129); main-only plan-detection commits are intentionally excluded — they reach staging via the normal main->staging path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
e575843 to
12d56dc
Compare
|
@greptile review the pr throughly |
055ae82 to
12d56dc
Compare
|
@greptile review This PR was rebased onto
All 527 tests pass; validated end-to-end on a real Ubuntu VM (multi-user + root). |
~/.junie/mcp/mcp.json needs 3 parent levels to resolve its reported path to ~ (home), matching every other global MCP config. Same fix applied to Windows Junie in PR #137. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
create_wrapper_script ran NPM_BIN=$(npm config get prefix 2>/dev/null)/bin with no failure guard, so under 'set -euo pipefail' a host without npm aborted the entire scheduler setup before the wrapper or cron entry was written. Guard the lookup with '|| true' and only append the bin dir when a prefix resolves. NPM_BIN now carries its own trailing colon when set and is empty otherwise, so an npm-absent host no longer leaves a leading ':' in the wrapper PATH (which would have put the CWD on PATH). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addressed (commit
|
|
@greptile review Addressed the remaining P1 from the last review (commit Also fixed an off-by-one in the Linux Junie MCP All review threads are resolved; 527 tests pass. |
|
@copilot check whether the cron runs once a day or for every login in macos. |
On macOS it’s configured as a LaunchAgent (not cron) with both:
So it runs once per day at 09:00 and also triggers when the agent loads (install/login). |
Summary
Adds full Linux support for
unbound discoverandunbound onboard, eliminatingValueError("Unsupported operating system: Linux")crashes on Ubuntu agent runners.47 new factory branches covering all 16 AI coding tools: Claude Code, Cursor, Windsurf, Codex, Gemini CLI, Cursor CLI, Cline, Roo Code, Antigravity, Kilo Code, OpenCode, OpenClaw, Replit, JetBrains, GitHub Copilot — detectors, rules extractors, MCP config extractors, settings extractors, and skills extractors.
Key Linux path adaptations
~/Library/Application Support/<IDE>/~/.config/<IDE>/~/Library/Application Support/JetBrains/~/.config/JetBrains/ioreg/ SMBIOS/etc/machine-id/Users/<username>/home/<username>dscl/etc/passwdMulti-user / root correctness — all P1 bugs fixed
Root scanning
get_linux_user_homes(): returns/home/*dirs AND/rootwhen root —/rootwas previously omitted when/homehad other usersscan_user_directories(): always checks/rootunconditionally when running as rootis_user_level_tool_dir(): recognises both/home/<user>/.<tool>and/root/.<tool>as user-scopeDocker / CI root-only containers (
/homeabsent)get_linux_user_homes(): falls back to[Path("/root")]when/homedoes not existget_all_users_linux(): falls back to["root"]when/homeis absent or emptyai_tools_discovery.py: both user-home loop sites special-case"root"→Path("/root")instead of building the non-existent/home/rootSystem path skipping
walk_for_tool_directoriesoverride using_LINUX_SKIP_SYSTEM_DIRS; the macOS version had/homeinSKIP_SYSTEM_DIRS, silently dropping all project-level configs under/home/*Skills extractors — user-scope classification
is_user_level_claude_subdir()withis_user_level_tool_dir()in all Linux skills extractors. The macOS version computesusers_root = Path.home().parent = /when running as root, so/home/*/skills dirs were misclassified as project-scope and duplicated.Extractor correctness (multi-user)
scan_user_directories(void_callback)to explicitget_linux_user_homes()loops —scan_user_directoriesshort-circuits on truthy returns, which would silently skip users if any callback ever returned oneglobal_*_dirnow computed per-user inside the loop (was always pointing at the first user or root)JetBrains multi-user deduplication
_filter_old_versionsnow applied per-user before extending the combined list. The previous cross-user dedup silently dropped any user whose IDE version was older than another user's (e.g. alice'sWebStorm2024.3was discarded because bob hadWebStorm2025.1, taking her Copilot plugin detection with it).mcp_extraction_helpersplatform guardselif platform.system() == "Linux"branchesAll Greptile P1 threads resolved (17 total)
~/.local/share→~/.config)mcp_extraction_helpers: Linux missing from all 5 platform guards; Cursor missing Linux guardglobal_*_dirduplication across users_detect_jetbrains_for_userrescanned all users per call/rootwas in_LINUX_SKIP_SYSTEM_DIRSget_linux_user_homesomitted/rooton multi-user systemsscan_user_directoriesreturnedNonewhen/homeabsent (Docker/CI)get_all_users_linuxreturned[]when/homeabsent (Docker/CI)is_user_level_tool_dirreturnedFalsefor/root/.<tool>scan_user_directoriesskipped/rooton multi-user machineswalk_for_tool_directoriesused macOS skip-list (/homeblocked all project configs);is_user_level_claude_subdirmisclassified dirs when root; Antigravity early return; 14 void-callback misusesai_tools_discovery.py: username"root"built/home/rootinstead of/root_filter_old_versions: cross-user dedup dropped users with older IDE versionsTest plan
pytest tests/ -v)/usr/bin/claudeGreptile Summary
This PR adds full Linux support for
unbound discoverandunbound onboardby introducing 47 new factory branches across all 16 AI coding tools, alinux_extraction_helpers.pymodule, and extendingsetup-scheduled-scan.shto install via systemd --user timer or crontab on Linux.linux_extraction_helpers.pyprovides Linux-awareget_linux_user_homes(),scan_user_directories(),is_user_level_tool_dir(),walk_for_tool_directories(), andshould_skip_system_path()— with correct/roothandling for Docker/CI root-only containers and multi-user systems.get_linux_user_homes()iteration to avoid the per-user early-return and cross-user dedup bugs fixed from previous reviews.setup-scheduled-scan.sh: Extended to support macOS + Linux with systemd timer (withPersistent=true) or crontab fallback;NPM_BINassignment now guarded with|| trueand conditional assignment to avoid aborting on npm-absent hosts and leaving a leading:inPATH; Junie MCPparent_levelscorrected from 2 to 3.Confidence Score: 5/5
Safe to merge — all previously-flagged blocking issues are resolved and no new defects were found.
The two remaining items called out in the reviewer instructions are both confirmed fixed: the NPM_BIN assignment is now guarded with || true and is only written when non-empty (no leading : in the wrapper PATH), and the Junie MCP parent_levels is correctly set to 3. Every other previously-flagged concern — JetBrains base directory, cross-user version deduplication, /root omission in get_linux_user_homes, sys.exit dead-code gate, Windsurf unused import, Cursor/Windsurf per-user global dir, GitHub Copilot rescan, mcp_extraction_helpers platform guards, and ai_tools_discovery.py root home path — has also been addressed in this commit. No new defects were introduced.
No files require special attention.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[setup-scheduled-scan.sh] --> B{OS?} B -->|macOS| C[install_macos - launchd daily 09:00] B -->|Linux| D[install_linux] D --> E{systemd user available?} E -->|Yes| F[install_linux_systemd - Persistent timer] E -->|No| G[install_linux_crontab - cron 0 9 daily] F -->|Fails| G C --> H[store_credentials_macos - macOS Keychain] D --> I[store_credentials_linux - ~/.unbound/scheduled-creds.json mode 0600] C & D --> J[create_wrapper_script - NPM_BIN guarded with OR true] J --> K{COMMAND?} K -->|discover| L[Download install.sh run with UNBOUND_API_KEY env var] K -->|onboard| M[Run unbound onboard with UNBOUND_API_KEY and UNBOUND_DISCOVERY_KEY] subgraph Linux Discovery N[main - enumerate_users] --> O[get_all_users_linux - /etc/passwd UID ge 1000] O --> P[resolve user_home - root to /root others to /home/user] P --> Q[detect_tool_for_user per user] Q --> R[47 Linux extractor factories] R --> S[get_linux_user_homes - always includes /root] endComments Outside Diff (1)
scripts/coding_discovery_tools/ai_tools_discovery.py, line 1763-1770 (link)main()are dead codeThe
sys.exit(3)guard at the top ofmain()exits for every non-Darwin/Windows platform before execution ever reaches theelif platform.system() == "Linux"branches added later in this same function (user enumeration viaget_all_users_linux(), root home-path resolution, etc.). On a Linux machine,unbound discoverwill always terminate at the guard and never run any of the Linux-specific extraction logic. Either the guard should be narrowed to exclude Linux (e.g.,if current_platform not in ("Darwin", "Windows", "Linux"):) so thatdiscoveractually works on Linux, or the unreachable Linux branches inmain()should be removed to match the declared intent that only macOS/Windows are supported for discovery.Reviews (28): Last reviewed commit: "fix: scheduler setup no longer aborts on..." | Re-trigger Greptile