feat(attach): add --target-repo and --dry-run flags for skill orchestration#1229
feat(attach): add --target-repo and --dry-run flags for skill orchestration#1229alishakawaguchi wants to merge 1 commit into
Conversation
…ration Enables an agent session running anywhere (parent dir, sibling repo, even a non-Entire-enabled directory) to be fanned out to one or more Entire-enabled repos without cd'ing or guessing what will be amended. - --target-repo <path>: chdir before the session group's worktree-root check so all downstream cwd-coupled lookups (openRepository, git rev-parse --git-common-dir, session state store path) resolve against the target. - --dry-run [--json]: emit the planned action (would_add_trailer / would_link_existing_in_head / would_skip_existing_in_state), checkpoint id, transcript size, and target repo without writing checkpoint metadata, saving session state, or amending HEAD. JSON output goes to stdout for clean piping. Short-circuits before transcript read so a multi-repo preview stays fast. Pairs with the entire-scope skill that consumes the JSON plan to render a per-repo preview, then re-runs without --dry-run on confirmation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds --target-repo and --dry-run (with optional --json) flags to entire session attach so an external orchestrator/skill can preview and fan an agent session out to multiple Entire-enabled repos without cd'ing into each one. --target-repo is handled by chdir'ing inside RunE (with a deferred restore) and by skipping the cwd-based worktree-root check in the session group's PersistentPreRunE. --dry-run short-circuits before transcript read and logging.Init, returning a structured plan (would_add_trailer / would_link_existing_in_head / would_skip_existing_in_state) either as human text or JSON on stdout.
Changes:
- Add
--target-repo,--dry-run,--jsonflags toentire session attach; reject--jsonwithout--dry-run. - Skip
logging.Initand side-effecting writes (transcript read, checkpoint metadata, session state, commit amend) on dry-run; emit adryRunResultplan to stdout (text or JSON). - Bypass the session group's cwd worktree-root precondition when
--target-repois provided; integration tests cover target-repo redirection, dry-run no-mutation, and--json-without---dry-runrejection.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| cmd/entire/cli/attach.go | New flags, dry-run short-circuits, chdir-to-target helper, structured dryRunResult emit. |
| cmd/entire/cli/sessions.go | Skip cwd worktree-root check in PersistentPreRunE when --target-repo is set. |
| cmd/entire/cli/integration_test/attach_test.go | Integration tests for --target-repo, --dry-run --json, and --json rejection. |
| if opts.DryRun { | ||
| // Determine checkpoint ID without reading the transcript so multi-repo | ||
| // previews stay fast (transcripts can be MBs). | ||
| plannedCheckpoint, isExistingCheckpoint := resolveCheckpointID(headCommit) | ||
| action := actionWouldAddTrailer | ||
| if isExistingCheckpoint { | ||
| action = actionWouldLinkExistingInHead | ||
| } | ||
| var existingTrailer string | ||
| if isExistingCheckpoint { | ||
| existingTrailer = plannedCheckpoint.String() | ||
| } | ||
| return emitDryRunResult(w, opts, dryRunResult{ | ||
| SessionID: sessionID, | ||
| TargetRepo: targetRepoPath(ctx), | ||
| HeadCommit: headCommit.Hash.String(), | ||
| ExistingTrailer: existingTrailer, | ||
| Action: action, | ||
| CheckpointIDPlanned: plannedCheckpoint.String(), | ||
| TranscriptBytes: transcriptFileBytes(transcriptPath), | ||
| Agent: string(ag.Type()), | ||
| }) | ||
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c9afa18. Configure here.
| _, isExistingCheckpoint := resolveCheckpointID(headCommit) | ||
| var existingTrailer string | ||
| if isExistingCheckpoint { | ||
| existingTrailer = cpID |
There was a problem hiding this comment.
Dry-run ExistingTrailer uses session-state ID instead of HEAD's
Low Severity
In the would_skip_existing_in_state dry-run path, resolveCheckpointID(headCommit) returns the actual checkpoint ID from HEAD's trailer as its first value, but this value is discarded (_). Then existingTrailer is set to cpID (from existingState.LastCheckpointID), which may differ from what's actually on HEAD. The other dry-run path at line 257 correctly captures and uses the returned ID (plannedCheckpoint.String()). When HEAD's trailer differs from the session state's checkpoint, ExistingTrailer in the JSON output will report the wrong value.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c9afa18. Configure here.
| ) | ||
| cmd.Dir = otherDir | ||
| cmd.Env = env.cliEnv() | ||
| outBytes, err := cmd.CombinedOutput() |
There was a problem hiding this comment.
Tests use raw exec.Command instead of execx.NonInteractive
Low Severity
The new TestAttach_TargetRepo_AmendsTargetNotCwd and TestAttach_DryRunJSON_NoMutation tests spawn the CLI binary via raw exec.Command without using execx.NonInteractive and without setting ENTIRE_TEST_TTY=0 in the child environment. The established pattern in this file (see RunCLIWithError in testenv.go) uses execx.NonInteractive to detach from the controlling TTY so CanPromptInteractively() returns false in the child.
Additional Locations (1)
Triggered by learned rule: Use execx.NonInteractive for subprocess spawning in tests and e2e
Reviewed by Cursor Bugbot for commit c9afa18. Configure here.
|
Closing — agent-only approach is sufficient. The skill that motivated this PR (entireio/skills#26, `session-crosslink`) now uses the shipped CLI (`cd && entire session attach --force`) and computes the preview locally by inspecting `git log -1`'s commit message and `.git/entire-sessions/.json` — same logic attach uses internally, no flag dependency. Tradeoffs the agent-only path accepts:
Both are acceptable for the one-shot use case. Re-opening this PR (or a variant) only makes sense if multi-repo skill performance becomes a concern, or if CI/scripting consumers want a structured `--dry-run --json` API. |
Rewrites the skill to use the shipped CLI (0.6.2) instead of the --target-repo / --dry-run / --json flags from entireio/cli#1229. - Step 3 computes the preview locally by inspecting git HEAD's commit message for an existing Entire-Checkpoint trailer and the target repo's .git/entire-sessions/<id>.json for an existing session-state checkpoint link. That mirrors the action logic attach uses internally. - Step 5 uses `cd <repo> && entire session attach <id> --agent <name> --force` per repo — one invocation per target with cwd as the repo selector. No CLI flag needed. Same five-step flow, same preview-then-confirm UX. Trades a few extra bash calls per candidate for zero CLI version coupling and no upstream PR dependency. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Entire-Checkpoint: 1f64c84a8821
* feat(scope): add scope skill to link cross-repo agent sessions Adds skills/scope/SKILL.md, an agent-invokable workflow for linking an agent session that ran outside the repo whose commits should record it — launched from a higher-level folder, a non-Entire repo, or one repo but editing another — to each affected Entire-enabled repo's HEAD commit. Flow: resolve the session id (cwd, sibling repo, or runtime transcript directory), discover affected repos from files_touched and user input, preview each with `entire session attach --target-repo --dry-run --json`, and amend on user confirmation. Depends on Entire CLI flags introduced in entireio/cli#1229: - entire session attach --target-repo <path> - entire session attach --dry-run [--json] Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * rename skill from scope to session-crosslink More specific name — pairs visually with session-handoff / session-to-skill and clarifies the action (linking one session across multiple repos) rather than the generic notion of scope. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Entire-Checkpoint: c97b84e870d3 * refactor(session-crosslink): drop dependency on unmerged CLI flags Rewrites the skill to use the shipped CLI (0.6.2) instead of the --target-repo / --dry-run / --json flags from entireio/cli#1229. - Step 3 computes the preview locally by inspecting git HEAD's commit message for an existing Entire-Checkpoint trailer and the target repo's .git/entire-sessions/<id>.json for an existing session-state checkpoint link. That mirrors the action logic attach uses internally. - Step 5 uses `cd <repo> && entire session attach <id> --agent <name> --force` per repo — one invocation per target with cwd as the repo selector. No CLI flag needed. Same five-step flow, same preview-then-confirm UX. Trades a few extra bash calls per candidate for zero CLI version coupling and no upstream PR dependency. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Entire-Checkpoint: 1f64c84a8821 * chore(session-crosslink): bump version, genericize examples, gate Strategy A on session_id - bump skills package version to 0.5.0 - replace Entire-specific repo names (cli, entire.io, entiredb, scope-skill) with foo/bar/baz/qux in SKILL.md examples - Strategy A now only proceeds when session_id is non-empty; addresses Cursor Bugbot finding on #26 about advancing to attach without a session id Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 88a7d0d6b430 * chore: sync remaining plugin manifests to 0.5.0 Brings gemini-extension.json (0.4.0) and the four plugin manifests (.cursor-plugin, .claude-plugin, .claude-plugin/marketplace, .codex-plugin — all at 0.3.0) up to 0.5.0 to match package.json. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 62841651aed5 --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>


Summary
Adds two flags to
entire session attachto enable agent sessions running outside an Entire-enabled repo (or across multiple repos) to be linked into the affected Entire-enabled repos withoutcd'ing or guessing what will be amended.--target-repo <path>: makes attach operate on the given repo instead of cwd. All downstream cwd-coupled lookups (openRepository,git rev-parse --git-common-dir, session state store path) resolve against the target. The session group'sPersistentPreRunEskips its cwd-based worktree-root check when this flag is set; the chdir + restore happens inside attach'sRunEvia a deferred restore.--dry-run [--json]: emits the planned action, checkpoint id, transcript size, target repo, and agent without writing checkpoint metadata, saving session state, or amending HEAD. The short-circuit fires before the transcript is read into memory (usesos.Statfor size) and beforelogging.Initopens a log file — so a multi-repo orchestrator preview stays fast and side-effect-free. JSON goes to stdout for clean piping; warnings stay on stderr.What this solves
Today the user runs an agent from a higher-level folder (e.g.
~/Projects/devenv) and the agent makes changes across multiple child repos. None of the child repos' commits get linked to that agent session, because Entire is repo-scoped and the parent-level launch leaves no session state in the children.The skill consuming these flags is in entireio/skills#26 (
session-crosslink) — it does the discovery and orchestration: find the running agent's session id and transcript, infer affected child repos fromfiles_touchedplus optional explicit input, callattach --dry-run --jsonper repo to render a preview, then on confirmation re-run without--dry-runto amend each child repo's HEAD with the checkpoint trailer.Investigation that motivated this: from a non-Entire-enabled parent,
entire session list --jsonreturns[]because session state is stored under each repo's git common dir; the parent has no store. Once a child repo has the session id,attachalready works — it resolves the transcript via the agent runtime's filesystem layout, not via any prior session store entry. The missing piece was a way to call attach against a specific repo (not cwd) and to preview before amending.Action values
--dry-run --jsonemits one of:would_add_trailer— HEAD has no checkpoint trailer; a new checkpoint will be created and the commit amended.would_link_existing_in_head— HEAD already has anEntire-Checkpoint:trailer; the session is added to that existing checkpoint.would_skip_existing_in_state— the session is already linked to a checkpoint in this repo's session store; no-op.Test plan
--target-repoamends the target repo's HEAD when CLI is run from an unrelated cwd, and session state lands in the target (not cwd).--dry-run --jsonreturns valid JSON on stdout, leaves HEAD hash + message unchanged, and writes no session state file.--jsonwithout--dry-runreturns an explanatory error.TestAttach_*,TestSession_*,TestReview_*integration suites pass.cmd/entire/cliunit tests pass.golangci-lint run --new-from-rev=mainreports 0 issues.Out of scope
entire transcript locatecommand — the skill currently hardcodes per-runtime transcript paths.--commit, which is a fast-follow if needed).--source-repo) — not needed; attach already resolves transcripts via the agent runtime's filesystem, not via any source repo's session store.🤖 Generated with Claude Code