Skip to content

feat(attach): add --target-repo and --dry-run flags for skill orchestration#1229

Closed
alishakawaguchi wants to merge 1 commit into
mainfrom
attach-target-repo
Closed

feat(attach): add --target-repo and --dry-run flags for skill orchestration#1229
alishakawaguchi wants to merge 1 commit into
mainfrom
attach-target-repo

Conversation

@alishakawaguchi
Copy link
Copy Markdown
Contributor

@alishakawaguchi alishakawaguchi commented May 19, 2026

Summary

Adds two flags to entire session attach to enable agent sessions running outside an Entire-enabled repo (or across multiple repos) to be linked into the affected Entire-enabled repos without cd'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's PersistentPreRunE skips its cwd-based worktree-root check when this flag is set; the chdir + restore happens inside attach's RunE via 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 (uses os.Stat for size) and before logging.Init opens 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 from files_touched plus optional explicit input, call attach --dry-run --json per repo to render a preview, then on confirmation re-run without --dry-run to amend each child repo's HEAD with the checkpoint trailer.

Investigation that motivated this: from a non-Entire-enabled parent, entire session list --json returns [] 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, attach already 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 --json emits 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 an Entire-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

  • New integration test: --target-repo amends the target repo's HEAD when CLI is run from an unrelated cwd, and session state lands in the target (not cwd).
  • New integration test: --dry-run --json returns valid JSON on stdout, leaves HEAD hash + message unchanged, and writes no session state file.
  • New integration test: --json without --dry-run returns an explanatory error.
  • Existing TestAttach_*, TestSession_*, TestReview_* integration suites pass.
  • cmd/entire/cli unit tests pass.
  • golangci-lint run --new-from-rev=main reports 0 issues.

Out of scope

  • A separate entire transcript locate command — the skill currently hardcodes per-runtime transcript paths.
  • Linking all commits in a child repo to the same session — attach links to HEAD (or --commit, which is a fast-follow if needed).
  • Cross-store session id lookup (--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

…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>
Copilot AI review requested due to automatic review settings May 19, 2026 19:15
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, --json flags to entire session attach; reject --json without --dry-run.
  • Skip logging.Init and side-effecting writes (transcript read, checkpoint metadata, session state, commit amend) on dry-run; emit a dryRunResult plan to stdout (text or JSON).
  • Bypass the session group's cwd worktree-root precondition when --target-repo is provided; integration tests cover target-repo redirection, dry-run no-mutation, and --json-without---dry-run rejection.

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.

Comment thread cmd/entire/cli/attach.go
Comment on lines +254 to +276
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()),
})
}
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread cmd/entire/cli/attach.go
_, isExistingCheckpoint := resolveCheckpointID(headCommit)
var existingTrailer string
if isExistingCheckpoint {
existingTrailer = cpID
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c9afa18. Configure here.

)
cmd.Dir = otherDir
cmd.Env = env.cliEnv()
outBytes, err := cmd.CombinedOutput()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Triggered by learned rule: Use execx.NonInteractive for subprocess spawning in tests and e2e

Reviewed by Cursor Bugbot for commit c9afa18. Configure here.

@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

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:

  • A few extra bash calls per candidate to inspect git/filesystem state instead of one `--dry-run --json` round-trip.
  • Drift risk: if attach's action-resolution logic changes server-side, the skill's preview can disagree until the skill is updated.

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.

alishakawaguchi added a commit to entireio/skills that referenced this pull request May 19, 2026
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
alishakawaguchi added a commit to entireio/skills that referenced this pull request May 20, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants