* feat(setup): persist ScrapeCreators API key on signup success
The GitHub device-auth signup (setup --github / --device-auth) returned
the ScrapeCreators API key as JSON to stdout but nothing persisted it, so
a successful signup never actually configured the paid sources.
- Add setup_wizard.write_api_key(): secret-safe (0o600), idempotent,
reuses _open_secret_append + _format_env_value (same path as
write_setup_config), and never clobbers an existing key.
- Add setup_wizard.mask_api_key(): prefix + last-4 display form.
- Wire both into the CLI --github/--device-auth branch: on
status==success, persist the key, set results['persisted'], and mask
api_key in stdout so the secret never lands in the host model's
captured Bash output.
Covers plan U2.
* feat(skill): consent-driven first-run onboarding in Step 0
The wizard fired but ran silently: the model invoked bare `setup`,
which extracts cookies + installs tools + writes SETUP_COMPLETE with
zero interaction. No consent before reading browser cookies, no macOS
Full Disk Access remediation, and the ScrapeCreators GitHub signup was
never offered.
Rewrite Step 0 as an ordered, consent-first sequence the model drives in
chat (the Python subprocess can't prompt):
1. Welcome
2. Ask cookie consent BEFORE reading; on decline run with
FROM_BROWSER=off (skip reads, still install yt-dlp + Digg)
3. macOS Full Disk Access remediation on permission-denied + one retry
4. Offer the ScrapeCreators GitHub signup every first run, consent
before launching the browser (setup --github)
5. Confirm active sources and proceed
Remove the misleading 'follow the wizard's prompts end-to-end' line and
add a named onboarding contract documenting why consent is conversational.
Copy avoids a hard credit count (grant is server-side). Adds
tests/test_onboarding_contract.py (7 contract assertions).
Covers plan U1.
* docs: document consent-driven first-run onboarding
- CONFIGURATION.md: new 'First-run onboarding' section covering the three
consent points (cookies, Full Disk Access, ScrapeCreators GitHub signup)
and automatic key persistence.
- AGENTS.md: extend the optional-sources rule to note onboarding is
consent-driven and model-led, and that setup --github persists the key.
- CHANGELOG.md: Unreleased entry (Added + Fixed) following #659.
Covers plan U3.
---------
Co-authored-by: Fredy Montero <fredymontero@Fredys-MacBook-Pro-2.local>
* feat(setup): auto-install digg-pp-cli during first-run NUX
Install the free, keyless digg-pp-cli via the Printing Press installer in
run_auto_setup, mirroring the yt-dlp/brew auto-install. The Digg source
already activates whenever the binary is on PATH (available_sources), so
this closes the only NUX gap. Degrades to a recommend-only outcome when
npx is unavailable; verifies on PATH and the Go bin dir; never raises.
Reports the outcome in the setup-complete summary.
* docs(nux): describe Digg auto-install in Step 0 + CONFIGURATION
Step 0 wizard narrative now mentions the best-effort digg-pp-cli install;
CONFIGURATION.md documents Digg as a free, keyless, read-only optional
source with its install command. Source display itself needs no change —
ACTIVE_SOURCES_LIST is built from the engine's --diagnose output, which
already maps digg->Digg.
* fix(setup): align Digg NUX with printing-press-library PATH contract
Use pinned @mvanhorn/printing-press-library@0.1.16, probe ~/.local/bin,
distinguish installed_off_path from engine-active installs, and extend
OpenClaw setup. Document the Hermes/OpenClaw agent PATH gap in AGENTS.md
and docs/solutions/.
* fix(setup): off-PATH Digg message names the actual install dir
The installed_off_path status hardcoded '$HOME/.local/bin' in its PATH
instruction even though the binary is probed across ~/.local/bin,
$GOPATH/bin, and ~/go/bin. A user whose digg-pp-cli landed in ~/go/bin
was told to add the wrong dir and couldn't activate Digg. Derive the dir
to add from where the binary was actually found. Resolves Greptile #590.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
* fix: resolve Windows compatibility issues in tests and subprocess execution
* fix: address Greptile review comments for Windows compatibility
---------
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
Add a pytest-discovered tests/conftest.py for the last30days scripts path and
remove duplicate per-file sys.path.insert boilerplate from tests.
Normalize affected imports to rely on the shared scripts path and remove the
now-unneeded E402 suppressions.
CI was running only test_plugin_contract.py and test_version_consistency.py
(2 of 84 test files), masking 13 rotted tests across 4 clusters. The suite is
fully offline-safe (1402 tests in ~7s without network), so the narrow scope
wasn't gating integration flakiness; it was just stale. validate.yml now runs
`uv run pytest` against the full suite.
Engine fix: store.findings_from_report is rerank-first. ranked_candidates is
the primary persistence path; hackernews/polymarket are unconditionally
supplemented from items_by_source because they rank poorly but matter for
watchlists. When ranked_candidates was empty (rerank failed or skipped),
reddit, x, and every other source were silently dropped. The supplement loop
now falls back to all sources only when ranked_candidates is empty; the normal
path is unchanged.
Test repairs:
- test_store.py (6) + test_watchlist_commands.py (2): cascade from the engine fix
- test_get_new_findings_filters_by_date (latent): local-time vs SQLite UTC
flake — switched to datetime.now(timezone.utc)
- TestPollDeviceAuth (3): mock_time.time side_effect lists too short after
impl added a last_reminder call — padded timeout test, pinned others to
return_value=0 (loops terminate via urlopen, not the clock)
- test_bare_run_emits_web_promo: engine reads ~/.config/last30days/.env, so
a contributor's saved EXA/PARALLEL key made grounding "available" and
suppressed the web promo. Also missing X made the "x" promo preempt "web".
Set LAST30DAYS_CONFIG_DIR="", subprocess cwd=tmpdir, XAI_API_KEY stub.