Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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 <parent-repo> --current` runs
- **THEN** the setup flow SHALL install or repair only `<parent-repo>`
- **AND** the nested repo SHALL not be traversed or modified during that run.
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 2 additions & 6 deletions src/cli/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)' : '';
Expand Down
4 changes: 2 additions & 2 deletions src/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)'],
Expand Down
22 changes: 22 additions & 0 deletions test/setup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down