diff --git a/README.md b/README.md index 1a57264..1c4243b 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,8 @@ Setup auto-installs into every nested git repo (e.g. `apps/*/.git`). Submodules ```sh gx setup --target /mainfolder gx setup --target /mainfolder --no-recursive +gx setup --target /mainfolder --current +gx doctor --target /mainfolder --current ``` ### Fresh repos + Docker Compose diff --git a/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/proposal.md b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/proposal.md new file mode 100644 index 0000000..6fd02f6 --- /dev/null +++ b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/proposal.md @@ -0,0 +1,18 @@ +## Why + +- `gx setup` recurses into nested repos by default, so operators need a short explicit way to keep a bootstrap or repair run scoped to just the target repo. +- `--no-recursive` already provides that behavior, but users naturally reach for `--current` after learning `gx doctor --current`. +- The current mismatch makes `setup` and `doctor` feel inconsistent even though they share the same repo-traversal model. + +## What Changes + +- Accept `--current` as a top-level-only alias for repo traversal in `gx setup`. +- Keep `gx doctor --current` working through the same shared traversal parser instead of a command-local special case. +- Update setup-facing operator copy to mention `--current` alongside `--no-recursive`. +- Add a regression proving nested repos under the target path stay untouched when `gx setup --current` is used. + +## Impact + +- Affected surface: `src/cli/main.js`, `src/context.js`, `test/setup.test.js`, `README.md`. +- Expected outcome: `gx setup --current` and `gx doctor --current` both scope work to the target repo only. +- Risk: low, because the alias reuses the existing non-recursive traversal path. diff --git a/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/specs/nested-repo-setup/spec.md b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/specs/nested-repo-setup/spec.md new file mode 100644 index 0000000..bc7411a --- /dev/null +++ b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/specs/nested-repo-setup/spec.md @@ -0,0 +1,10 @@ +## ADDED Requirements + +### Requirement: setup current alias limits bootstrap to the target repo +The system SHALL support `gx setup --current` as an alias for the existing top-level-only traversal path. + +#### Scenario: current alias skips nested repo installs +- **GIVEN** a parent repo contains a nested standalone git repo +- **WHEN** `gx setup --target --current` runs +- **THEN** the setup flow SHALL install or repair only `` +- **AND** the nested repo SHALL not be traversed or modified during that run. diff --git a/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/tasks.md b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/tasks.md new file mode 100644 index 0000000..b7c3e88 --- /dev/null +++ b/openspec/changes/agent-codex-setup-current-single-repo-alias-2026-04-22-13-48/tasks.md @@ -0,0 +1,34 @@ +## Definition of Done + +This change is complete only when **all** of the following are true: + +- Every checkbox below is checked. +- The agent branch reaches `MERGED` state on `origin` and the PR URL + state are recorded in the completion handoff. +- If any step blocks, add a `BLOCKED:` line under section 4 and stop. + +Handoff: change=`agent-codex-setup-current-single-repo-alias-2026-04-22-13-48`; branch=`agent/codex/setup-current-single-repo-alias-2026-04-22-13-48`; scope=`OpenSpec change docs, setup traversal parsing, setup regression coverage, operator-facing setup copy`; action=`accept --current as the top-repo-only setup alias and keep traversal messaging aligned with doctor`. + +## 1. Specification + +- [x] 1.1 Capture the `gx setup --current` alias scope and acceptance criteria. +- [x] 1.2 Add normative OpenSpec coverage for the setup single-repo alias behavior. + +## 2. Implementation + +- [x] 2.1 Accept `--current` as a setup alias for the existing top-level-only traversal path. +- [x] 2.2 Update setup-facing help/output/docs to advertise `--current`. +- [x] 2.3 Add a regression proving nested repos under the target path stay untouched. + +## 3. Verification + +- [x] 3.1 Run `node --check bin/multiagent-safety.js`. Result: passed. +- [x] 3.2 Run `node --test test/setup.test.js`. Result: passed (`42/42` tests, including `setup --current limits install to the top-level repo`). +- [x] 3.3 Run `openspec validate agent-codex-setup-current-single-repo-alias-2026-04-22-13-48 --type change --strict`. Result: passed. +- [x] 3.4 Run `openspec validate --specs`. Result: passed (`No items found to validate.`). + +## 4. Cleanup + +- [x] 4.1 Commit the change with a Lore commit message. Result: `d942b84`. +- [ ] 4.2 Run `gx branch finish --branch agent/codex/setup-current-single-repo-alias-2026-04-22-13-48 --base main --via-pr --wait-for-merge --cleanup`. +- [ ] 4.3 Record the PR URL and final merge state (`MERGED`) in the completion handoff. +- [ ] 4.4 Confirm the sandbox worktree and branch refs are gone after cleanup. diff --git a/src/cli/main.js b/src/cli/main.js index 1f20901..6f349dc 100755 --- a/src/cli/main.js +++ b/src/cli/main.js @@ -2013,7 +2013,7 @@ function parseRepoTraversalArgs(rawArgs, defaults) { for (let index = 0; index < rawArgs.length; index += 1) { const arg = rawArgs[index]; - if (arg === '--no-recursive' || arg === '--no-nested' || arg === '--single-repo') { + if (arg === '--no-recursive' || arg === '--no-nested' || arg === '--single-repo' || arg === '--current') { traversalDefaults.recursive = false; continue; } @@ -2088,10 +2088,6 @@ function parseDoctorArgs(rawArgs) { for (let index = 0; index < rawArgs.length; index += 1) { const arg = rawArgs[index]; - if (arg === '--current') { - forwardedArgs.push('--single-repo'); - continue; - } if (arg === '--verbose-auto-finish') { doctorDefaults.verboseAutoFinish = true; continue; @@ -6689,7 +6685,7 @@ function setup(rawArgs) { if (discoveredRepos.length > 1) { console.log( - `[${TOOL_NAME}] Detected ${discoveredRepos.length} git repos under ${topRepoRoot}. Installing into each (use --no-recursive to limit to the top-level).`, + `[${TOOL_NAME}] Detected ${discoveredRepos.length} git repos under ${topRepoRoot}. Installing into each (use --no-recursive or --current to limit to the top-level).`, ); for (const repoPath of discoveredRepos) { const marker = repoPath === topRepoRoot ? ' (top-level)' : ''; diff --git a/src/context.js b/src/context.js index f64dbf3..623dc8c 100644 --- a/src/context.js +++ b/src/context.js @@ -322,8 +322,8 @@ const SUGGESTIBLE_COMMANDS = [ ]; const CLI_COMMAND_DESCRIPTIONS = [ ['status', 'Show GitGuardex CLI + service health without modifying files'], - ['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target)'], - ['doctor', 'Repair drift + verify (auto-sandboxes on protected main)'], + ['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target, --current)'], + ['doctor', 'Repair drift + verify (flags: --target, --current; auto-sandboxes on protected main)'], ['branch', 'CLI-owned branch workflow surface (start/finish/merge)'], ['locks', 'CLI-owned file lock surface (claim/allow-delete/release/status/validate)'], ['worktree', 'CLI-owned worktree cleanup surface (prune)'], diff --git a/test/setup.test.js b/test/setup.test.js index fcbb467..10d1c21 100644 --- a/test/setup.test.js +++ b/test/setup.test.js @@ -703,6 +703,28 @@ test('setup --no-recursive limits install to the top-level repo', () => { ); }); +test('setup --current limits install to the top-level repo', () => { + const topDir = initRepo(); + const nestedA = path.join(topDir, 'apps', 'a'); + fs.mkdirSync(nestedA, { recursive: true }); + const initResult = runCmd('git', ['init', '-b', 'dev'], nestedA); + assert.equal(initResult.status, 0, initResult.stderr); + + const result = runNode( + ['setup', '--target', topDir, '--no-global-install', '--current'], + topDir, + ); + assert.equal(result.status, 0, result.stderr || result.stdout); + assert.doesNotMatch(result.stdout, /Detected \d+ git repos under/); + + assert.equal(fs.existsSync(path.join(topDir, 'AGENTS.md')), true); + assert.equal( + fs.existsSync(path.join(nestedA, 'AGENTS.md')), + false, + 'nested repo must not be touched when --current is set', + ); +}); + test('setup refreshes initialized protected main through a sandbox and prunes it', () => { const repoDir = initRepoOnBranch('main');