feat: cwd catalog hook + pre-login GTM getting-started skill#61
Conversation
Design for the praxis-MCP read posture: an agent-owned ~/.praxis/ig-checkouts.json memory (git-remote -> local checkout) so catalog node file:line resolves to a real local file without re-discovery, plus SessionStart/CwdChanged hooks (wired at `praxis login`) that nudge toward use-ig when cwd matches a remembered checkout. Mirrors ig's hook pattern; quiet until the memory is populated. Server git/sha enrichment and `praxis ig workspace` verbs are deferred follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the praxis-MCP read posture a catalog node's source path is relative to
its member's repo root, but the server doesn't know where (or whether) that
repo is checked out here. This adds the host-local record that closes the
gap, plus cwd hooks that surface it.
- internal/igcheckouts: the ~/.praxis/ig-checkouts.json memory (git-remote ->
{path, member, catalogs}), canonical git-URL matching (https/ssh/scp forms
collapse), and the nudge builder. catalogs is a LIST — one checkout can be a
member of several catalogs (control-plane in both capillary-cloud and
saas-cp), unioned as the agent encounters each.
- cmd/ig ig hook <session-start|cwd-changed>: hidden handler wired into Claude
Code settings.json. Nudges toward use-ig only when cwd matches a remembered
checkout; silent + exit 0 otherwise (never blocks a session), deduped per
session. Unlike `ig hook` it stays quiet until the memory is populated.
- internal/claudehooks: additive/idempotent SessionStart+CwdChanged wiring
(one praxis entry per event, stale path refreshed, foreign hooks untouched,
invalid JSON refused, .bak kept). Mirrors ig's own hook wiring.
- praxis login wires the hooks (claude-code host, user- or project-scoped);
praxis logout unwires them.
- use-ig SKILL.md: "Resolving a node to a local file" — check memory, discover,
remember (union catalogs), mind staleness (re-anchor by symbol, not line).
Deferred: server per-member git/sha enrichment for precise staleness, and
`praxis ig workspace` verbs. Spec: docs/superpowers/specs/2026-07-15-*.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds a hidden ChangesIG hook integration
Pre-login bootstrap
Sequence Diagram(s)sequenceDiagram
participant ClaudeCode
participant Praxis
participant Git
participant IGCatalog
ClaudeCode->>Praxis: Run praxis ig hook event
Praxis->>Git: Resolve origin for cwd
Praxis->>IGCatalog: Request claims for canonical origin
IGCatalog-->>Praxis: Return catalog names
Praxis-->>ClaudeCode: Emit additionalContext or remain silent
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Redesign following review. The first cut gated the nudge on an
LLM-maintained ~/.praxis/ig-checkouts.json keyed by git URL. Two problems:
1. We can't rely on an LLM to keep local JSON correct, so an absent/stale
file would silence the nudge forever. Membership must be authoritative.
2. The repo is not a stable member identity: member names differ across
catalogs, and a monorepo hosts several members from one repo.
New posture:
- The hook (internal/ighook + `praxis ig hook`) is GENERIC: it canonicalizes
cwd's git origin and asks the SERVER (igcatalog.Claims, = `praxis ig claims`)
which catalogs claim the repo, nudging if any. No agent-maintained file is
read. ~2.5s timeout, one successful check per repo per session, silent on
no-auth/offline/non-git. Replaces internal/igcheckouts (deleted).
- ~/.praxis/ig-checkouts.json survives ONLY as the agent's optional resolution
scratchpad (keyed "<catalog>/<member>" -> {repo, path, dir} to handle
member-name divergence + monorepo subdirs); no Go reads it, and the skill
section is trimmed to a short, non-prescriptive note.
Node resolution: abs = path / dir / <node-rel-path> (graphify builds a member
from its subdir, so paths are relative to dir, not the git root).
Verified: unit tests (member->nudge, non-member/error-retryable/dedup, hook
wiring), fail-silent smoke of the built binary (~1s, exit 0), and a live
`praxis ig claims` read against the real gateway returning cleanly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
cmd/ig_hook_test.go (1)
1-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd direct coverage for
igHookCmd.RunE, not justrunIgHook.All tests here exercise the pure
runIgHookhelper; the actual Cobra wiring — stdin JSON parsing, theos.Getwd()fallback,igHookEventName's unknown-arg error path, and stdout printing — has no direct test.Would you like me to draft a
TestIgHookCmdRunEusingcmd.SetOut(&buf)/cmd.SetIn(strings.NewReader(...))and callingRunEdirectly, covering: valid stdin → nudge printed, unknown event arg → error returned, and empty stdin → cwd fallback?
As per path instructions, "This includescmd/*cobra commands — test them withcmd.SetOut(&buf)and callRunE/Rundirectly."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/ig_hook_test.go` around lines 1 - 109, Add direct tests for igHookCmd.RunE using Cobra command output and input buffers, rather than only testing runIgHook. Cover valid stdin producing a printed nudge, an unknown event argument returning an error through igHookEventName, and empty stdin falling back to the current working directory; configure command I/O with SetOut and SetIn.Source: Coding guidelines
cmd/ig_hook.go (1)
44-50: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winBound
repoOriginURLwith a timeout.exec.Commandcan block indefinitely, and this hook is meant to never stall a session. A shortexec.CommandContexttimeout here would keep the localgit remote get-url originlookup from hanging the hook.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/ig_hook.go` around lines 44 - 50, Update repoOriginURL to create a short-lived context with a timeout and use exec.CommandContext for the git remote lookup. Ensure the context is canceled and preserve the existing empty-string fallback and trimmed URL behavior when the command fails or times out.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/login_setup.go`:
- Around line 251-289: Update wirePraxisHooks to resolve a fresh, unscoped
claude-code harness instead of using the possibly project-scoped entry from
hosts when deriving settingsPath, keeping hook wiring non-fatal. Add a
project-scoped login/logout regression test that verifies the hook is removed
after logout and fails before this fix.
In `@cmd/logout.go`:
- Line 83: Update both call sites of unwirePraxisHooks to capture its cleanup
failure and append the resulting warning/error to the existing warnings
collection in JSON mode, ensuring the response reports unsuccessful hook removal
instead of silently succeeding. Preserve non-JSON behavior, and add a JSON-mode
regression test covering the cleanup-failure path.
In `@internal/claudehooks/claudehooks_test.go`:
- Around line 89-105: Extend TestInstallRefreshesStalePath into a table-driven
test covering both the existing path and an executable path containing spaces,
such as /Applications/Praxis CLI/praxis. For each case, install the stale path,
refresh with the target path, and assert the persisted SessionStart command
remains executable and recognizable while preserving the changed result check.
In `@internal/claudehooks/claudehooks.go`:
- Around line 147-157: Update the settings persistence flow to write both the
backup created from raw and the newly created settings file with mode 0600. In
the backup block, stop discarding os.WriteFile errors and return the failure
instead of continuing; preserve the existing MarshalIndent and
directory-creation behavior.
- Around line 35-48: The hook command serialization and ownership check must
support executable paths containing spaces. In
internal/claudehooks/claudehooks.go lines 35-48, update command and
isPraxisHookCommand to use a safe, matching path encoding/parsing strategy
rather than strings.Fields; in internal/claudehooks/claudehooks_test.go lines
89-105, add a table-driven regression case covering a praxis executable path
with spaces.
- Around line 1-5: Update the package documentation comment for claudehooks to
describe that hooks resolve catalog membership from canonical repository
identity and use server-authoritative claims. Remove references to remembered
checkouts and internal/igcheckouts, while preserving the explanation of the
Claude Code-specific settings.json integration.
- Around line 53-77: Update listUpsert to scan the entire list, retain exactly
one Praxis hook for the event, refresh its command to want, and remove any
additional matching hooks before returning the updated list and change status.
In internal/claudehooks/claudehooks_test.go lines 71-87, add duplicate hook
entries and assert that only one remains after upsert.
---
Nitpick comments:
In `@cmd/ig_hook_test.go`:
- Around line 1-109: Add direct tests for igHookCmd.RunE using Cobra command
output and input buffers, rather than only testing runIgHook. Cover valid stdin
producing a printed nudge, an unknown event argument returning an error through
igHookEventName, and empty stdin falling back to the current working directory;
configure command I/O with SetOut and SetIn.
In `@cmd/ig_hook.go`:
- Around line 44-50: Update repoOriginURL to create a short-lived context with a
timeout and use exec.CommandContext for the git remote lookup. Ensure the
context is canceled and preserve the existing empty-string fallback and trimmed
URL behavior when the command fails or times out.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f99a8922-ce50-4cd7-886e-f15fb439c132
📒 Files selected for processing (12)
cmd/ig.gocmd/ig_hook.gocmd/ig_hook_test.gocmd/login_setup.gocmd/logout.godocs/superpowers/specs/2026-07-15-use-ig-local-checkout-memory-design.mdinternal/claudehooks/claudehooks.gointernal/claudehooks/claudehooks_test.gointernal/ighook/ighook.gointernal/ighook/ighook_test.gointernal/skillinstall/embedded/use-ig/SKILL.mdinternal/skillinstall/embedded_test.go
- login/logout symmetry: wire hooks at USER level always (fresh unscoped claude-code harness), even under --local. The hook resolves the active profile per-cwd at run time, so one user-level hook serves every project and logout can always remove it — a project-scoped wire would strand a hook logout never touches. Regression test added. - logout: unwirePraxisHooks now returns a warning that both call sites append to the JSON `warnings` envelope, so automation isn't told logout succeeded while hooks remain. Regression test added. - claudehooks: shell-quote the executable path in the hook command and parse ownership quote-aware, so a praxis path containing spaces both runs and is recognized as ours (idempotent refresh, not duplicate). Table-driven test. - claudehooks: normalize to exactly one praxis entry per event (collapse any accidental duplicates), not just refresh the first. Test added. - claudehooks: write the settings backup and any newly-created settings file 0600 (a Claude settings file can hold credentials) and propagate backup-write failures. Perms asserted in test. - claudehooks: package doc updated to the server-authoritative design (drop the stale internal/igcheckouts reference). Verified live (rebuilt binary): nudge still fires; isolated login writes the quoted user-level hook; logout removes it. go test ./... + vet green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make a freshly-installed praxis discoverable to the AI host BEFORE any login, so it knows what Praxis by Facets does, where to sign up, and how to log in. Today skills only install at `praxis login`, so a just-installed praxis is invisible (the skill that would teach the host to log in only lands after login). - internal/skillinstall: `praxis-getting-started`, an embedded (go:embed), network-free meta-skill — the 4 headline capabilities (migrate cloud A→B, bring manual infra under IaC, "you code, Praxis operates", understand code↔infra), sign up at facets.cloud/signup, and `praxis login --url https://<your-account-id>.console.facets.cloud`. It's a meta-skill, so it survives logout (host can always find its way back to login). New `BootstrapSkillNames()` = the no-auth-installable subset. - cmd `praxis setup` (hidden): the no-auth install primitive — detect AI hosts, install the bootstrap skill(s). (`init`, the obvious name, was removed in the major cleanup and stays gone — root_test.go.) - cmd first-run (Execute-time, marker-gated ~/.praxis/.bootstrap-v1): auto- installs on the first human command, covering `praxis update` + raw downloads the cask hook can't reach. Skips machine paths (`ig hook`, `mcp`, completion…) so a hot/side-effect-sensitive invocation never triggers a skill write. - .goreleaser.yml: brew cask post-install calls `praxis setup` (must_succeed: false) so `brew install` lands the skill with zero user steps. Verified: setup installs the skill (4 host targets) with correct GTM copy; first-run installs on `status` + writes the marker; `ig hook` does not install; setup hidden from --help. go test ./... + vet green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/setup.go`:
- Around line 120-127: Update firstPositional to consume values for value-taking
persistent flags such as --profile before selecting the command, so its result
is the actual subcommand rather than "prod"; preserve positional argument
detection for other flags and add a regression test covering `praxis --profile
prod ig hook session-start`, including the documented active-profile resolution
order.
- Around line 76-83: Update cmd/setup.go lines 76-83 in installBootstrapSkills
to return state distinguishing no detected hosts from a completed installation;
keep the .bootstrap-v1 marker absent at lines 60 and 168-170 unless installation
actually completed, so first-run retries after a host is later available. In
cmd/setup_test.go lines 120-147, inject detected hosts and add coverage for the
no-host retry path, invoking the Cobra command through SetOut and RunE/Run
directly.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2b0a7f09-bb9e-499c-a924-7246abe707f9
📒 Files selected for processing (8)
.goreleaser.ymlcmd/root.gocmd/setup.gocmd/setup_test.godocs/superpowers/specs/2026-07-16-praxis-gtm-bootstrap-skill-design.mdinternal/skillinstall/bootstrap_test.gointernal/skillinstall/dummy.gointernal/skillinstall/getting-started.md
- Write the first-run marker ONLY after a real install (n > 0). A brew install on a machine with no AI host yet returned (0, nil) but still marked done, so a Claude/Codex installed later was permanently skipped. No-host and failures now leave the marker unwritten and retry once a host appears. (firstRunBootstrap takes a count-returning install; setup marks only when n > 0.) Regression test. - firstPositional now skips value-taking flags' values, so `praxis --profile prod ig hook` classifies as `ig` (machine path, skipped) not `prod`. Otherwise first-run would write skills during a hook. Regression cases. go test ./... + vet green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two related pieces of the praxis-MCP posture, bundled into one release.
1. Generic cwd catalog hook (
praxis ig hook)Nudges the agent toward the
use-igskill when cwd's git repo is an ig catalog member — asked of the server (praxis ig claimsover the git origin), never gated on agent-maintained JSON. Wired atpraxis login(user-level), unwired atpraxis logout. ~2.5s timeout, one successful lookup per repo per session, silent + exit 0 on no-auth/offline/non-git. Plus a shortuse-igskill section for resolving a node's repo-relative path to a local file (monorepo-aware). Addresses all 7 CodeRabbit findings (user-level wiring, JSON-surfaced logout warnings, spaced-path quoting, duplicate collapse, 0600 backups, doc).2. Pre-login GTM getting-started skill
Make a freshly-installed praxis discoverable to the AI host before any login — today skills only install at
praxis login, so a just-installed praxis is invisible and nothing tells the host to log in.praxis-getting-started— embedded, network-free meta-skill (Praxis by Facets): the 4 headline capabilities (migrate cloud A→B · manual infra → IaC · "you code, Praxis operates" · understand code↔infra), sign up atfacets.cloud/signup,praxis login --url https://<your-account-id>.console.facets.cloud. Meta-skill → survives logout.praxis setup(hidden) — the no-auth install primitive;initstays removed.ig hook,mcp, completion) so it never writes mid-session.praxis setup(must_succeed: false) sobrew installlands the skill with zero steps.Testing
go test ./...+go vet ./...green;gofmtclean;goreleaser-checkCI validates the cask.praxis ig claims control-plane→[capillary-cloud]; nudge fires on a control-plane checkout; isolated login wires the quoted user-level hook, logout removes it.praxis setupinstalls the skill (4 host targets) with correct copy; first-run installs onstatus+ writes marker;ig hookdoes not install;setuphidden from--help.Specs:
docs/superpowers/specs/2026-07-15-use-ig-local-checkout-memory-design.md,docs/superpowers/specs/2026-07-16-praxis-gtm-bootstrap-skill-design.md.🤖 Generated with Claude Code
Summary by CodeRabbit
praxis setupbootstrap that installs embedded “getting started” guidance into detected AI hosts; Homebrew now runs it post-install.use-igguidance for local-checkout memory, plus added/expanded related design specifications.