@imdeadpool/guardex 7.0.26 → 7.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -111,6 +111,16 @@ gx setup
111
111
 
112
112
  That's it. Install and update via `@imdeadpool/guardex`. Setup installs the minimal repo footprint: managed hook shims, repo-local state, AGENTS wiring, OpenSpec/caveman/OMX scaffolding, and a small set of repo-local helper assets. Aliases: `gx` (preferred), `gitguardex` (full), `guardex` (legacy compatibility).
113
113
 
114
+ ### Install surfaces at a glance
115
+
116
+ | Goal | Command | Installs |
117
+ | --- | --- | --- |
118
+ | Global Guardex CLI | `npm i -g @imdeadpool/guardex` | `gx`, `gitguardex`, `guardex`, and the CLI runtime |
119
+ | User-level Codex/Claude companions | `gx install-agent-skills` | `~/.codex/skills/gitguardex/SKILL.md`, `~/.codex/skills/guardex-merge-skills-to-dev/SKILL.md`, and `~/.claude/commands/gitguardex.md` |
120
+ | Generic repo skill catalog | `npx skills add recodee/` or `npx skills add recodee/gitguardex` | the repo-root `skills/` catalog for agents that support the generic installer |
121
+
122
+ These paths are complementary, not chained together. `npm i -g @imdeadpool/guardex` does not auto-run `npx skills add ...`.
123
+
114
124
  ---
115
125
 
116
126
  ## How `AGENTS.md` and `CLAUDE.md` are handled
@@ -140,7 +150,14 @@ That's it. Install and update via `@imdeadpool/guardex`. Setup installs the mini
140
150
  </div>
141
151
 
142
152
  > [!NOTE]
143
- > In this repo, `CLAUDE.md` is a symlink to `AGENTS.md`, so Claude reads the same contract. Optional Codex/Claude companion files still install at the user level with `gx install-agent-skills`, while the generic repo skill catalog is available through `npx skills add recodeee/` or directly via `npx skills add recodeee/gitguardex`.
153
+ > In this repo, `CLAUDE.md` is a symlink to `AGENTS.md`, so Claude reads the same contract as Codex.
154
+ >
155
+ > Keep the install paths separate:
156
+ > - `npm i -g @imdeadpool/guardex` installs the Guardex CLI.
157
+ > - `gx install-agent-skills` installs the user-level Codex/Claude companion files.
158
+ > - `npx skills add recodee/` or `npx skills add recodee/gitguardex` installs the generic repo skill catalog.
159
+ >
160
+ > The global npm install does not auto-run the generic `npx skills add ...` flow.
144
161
 
145
162
  ### Decision flow
146
163
 
@@ -461,17 +478,19 @@ Repo: <https://github.com/Yeachan-Heo/oh-my-claudecode>
461
478
 
462
479
  ### GitGuardex skills - install the repo skill catalog through `npx skills`
463
480
 
464
- For agents that already support the generic `skills` installer flow, GitGuardex now exposes its repo skill catalog directly. You can start from the broader `recodeee` source or jump straight into this repo's catalog.
481
+ For agents that already support the generic `skills` installer flow, GitGuardex exposes its repo skill catalog directly. Use it as a separate install path after the CLI install. You can start from the broader `recodee` source or jump straight into this repo's catalog.
465
482
 
466
483
  ```sh
467
- npx skills add recodeee/
484
+ npx skills add recodee/
468
485
  ```
469
486
 
470
487
  ```sh
471
- npx skills add recodeee/gitguardex
488
+ npx skills add recodee/gitguardex
472
489
  ```
473
490
 
474
- This repo currently exposes `gitguardex` and `guardex-merge-skills-to-dev` through that flow. If the picker does not show a separate `guardex` skill, that is expected: `guardex` remains the legacy CLI alias, while the repo skill itself is named `gitguardex`. Use `gx install-agent-skills` when you want the Codex/Claude user-home startup files instead of the generic `skills` catalog.
491
+ This repo currently exposes `gitguardex` and `guardex-merge-skills-to-dev` through that flow. If the picker does not show a separate `guardex` skill, that is expected: `guardex` remains the legacy CLI alias, while the repo skill itself is named `gitguardex`.
492
+
493
+ Need the Codex/Claude user-home startup files instead? Run `gx install-agent-skills`. Need the generic repo catalog? Run `npx skills add ...`. They solve different setup surfaces.
475
494
 
476
495
  Repo: <https://github.com/recodeee/gitguardex>
477
496
  [![GitHub stars](https://img.shields.io/github/stars/recodeee/gitguardex?style=social)](https://github.com/recodeee/gitguardex)
@@ -626,7 +645,7 @@ vscode/guardex-active-agents/README.md
626
645
 
627
646
  Legacy compatibility note: older repos may still contain repo-local workflow scripts under `scripts/`. Direct `gx branch ...`, `gx locks ...`, `gx finish`, `gx cleanup`, `gx merge`, and `gx migrate` do not require them. `gx migrate` removes those leftover workflow shims by default. The CLI still honors repo-local `scripts/review-bot-watch.sh` and `scripts/codex-agent.sh` when they are already present so older repos can keep working during migration.
628
647
 
629
- Optional Codex/Claude user-level companions still install with `gx install-agent-skills`; the generic repo skill catalog is available with `npx skills add recodeee/` or directly via `npx skills add recodeee/gitguardex`. Neither path copies those user-home files into each repo.
648
+ Optional Codex/Claude user-level companions still install with `gx install-agent-skills`; the generic repo skill catalog is available with `npx skills add recodee/` or directly via `npx skills add recodee/gitguardex`. Neither path copies those user-home files into each repo, and the global Guardex npm install does not trigger the `npx skills add ...` path for you.
630
649
 
631
650
  ---
632
651
 
@@ -689,9 +708,21 @@ npm pack --dry-run
689
708
  <details>
690
709
  <summary><strong>v7.x</strong></summary>
691
710
 
711
+ ### v7.0.28
712
+ - Bumped `@imdeadpool/guardex` from `7.0.27` to `7.0.28` so the CLI help redesign can ship on a fresh npm version.
713
+ - `gx --help` and `gx` (no args) now render commands as a grouped catalog (Setup & health / Branch workflow / Coordination / Agents & reports / Meta) with short group descriptions, so the top of the help screen shows the newcomer path instead of a flat 20-row list.
714
+ - Added a three-step Quickstart block (`gx setup` → `gx branch start "<task>" "<agent>"` → `gx branch finish --via-pr --wait-for-merge --cleanup`) to both help surfaces so the intended install/setup sequence is visible before the full command reference.
715
+ - Exposed `CLI_COMMAND_GROUPS` and `CLI_QUICKSTART_STEPS` from `src/context.js` (with `CLI_COMMAND_DESCRIPTIONS` derived from the grouped source of truth), giving future help tooling and integrations a structured way to iterate the catalog without re-parsing flat rows.
716
+
717
+ ### v7.0.27
718
+ - Bumped `@imdeadpool/guardex` from `7.0.26` to `7.0.27` so npm can publish a fresh version after `7.0.26` was already taken on the registry.
719
+ - The shipped `agent-branch-start.sh` copies now keep the startup auto-transfer path alive under `set -o pipefail`, so Guardex can still restore moved changes back to the protected checkout when branch startup hits a later failure.
720
+ - The shipped `agent-branch-finish.sh` copies now keep PR-only finish runs from opening temporary integration repos, so maintainers can finish directly through the existing PR lane without extra temp-repo churn.
721
+ - Keep the release scoped to version metadata for the already-merged `main` payload; no additional runtime behavior changes are introduced in this release lane.
722
+
692
723
  ### v7.0.26
693
724
  - Bumped `@imdeadpool/guardex` from `7.0.25` to `7.0.26` so npm can publish a fresh version after `v7.0.25` reached GitHub Releases while the registry stayed on `7.0.24`.
694
- - README now documents both `npx skills add recodeee/` and `npx skills add recodeee/gitguardex`, and explains why the picker shows `gitguardex` instead of a separate `guardex` skill.
725
+ - README now documents both `npx skills add recodee/` and `npx skills add recodee/gitguardex`, clarifies that the global Guardex npm install does not auto-run the generic skills installer, and explains why the picker shows `gitguardex` instead of a separate `guardex` skill.
695
726
  - Keep the release scoped to version metadata plus the already-merged README installer guidance on `main`; no additional CLI/runtime behavior changed in this lane.
696
727
 
697
728
  ### v7.0.25
package/SECURITY.md CHANGED
@@ -8,7 +8,7 @@ Only the latest published GitGuardex CLI build is supported for security fixes.
8
8
 
9
9
  Please report security issues privately by opening a GitHub security advisory:
10
10
 
11
- - https://github.com/recodeecom/multiagent-safety/security/advisories/new
11
+ - https://github.com/recodeee/gitguardex/security/advisories/new
12
12
 
13
13
  If advisories are unavailable, open a private report via GitHub issue contact details and avoid posting exploit details publicly.
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "7.0.26",
3
+ "version": "7.0.31",
4
4
  "description": "Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman.",
5
5
  "license": "MIT",
6
6
  "preferGlobal": true,
@@ -68,10 +68,10 @@
68
68
  "access": "public"
69
69
  },
70
70
  "devDependencies": {
71
- "fast-check": "^3.23.2"
71
+ "fast-check": "3.23.2"
72
72
  },
73
73
  "dependencies": {
74
- "jsonc-parser": "^3.3.1",
75
- "semver": "^7.7.4"
74
+ "jsonc-parser": "3.3.1",
75
+ "semver": "7.7.4"
76
76
  }
77
77
  }
package/src/cli/main.js CHANGED
@@ -146,8 +146,10 @@ const {
146
146
  colorizeDoctorOutput,
147
147
  statusDot,
148
148
  printToolLogsSummary,
149
+ getInvokedCliName,
149
150
  usage,
150
151
  formatElapsedDuration,
152
+ startTransientSpinner,
151
153
  compactAutoFinishPathSegments,
152
154
  detectRecoverableAutoFinishConflict,
153
155
  printAutoFinishSummary,
@@ -889,6 +891,80 @@ function parseBooleanLike(raw) {
889
891
  return null;
890
892
  }
891
893
 
894
+ function autoDoctorEnabledForCurrentSession() {
895
+ const explicit = parseBooleanLike(process.env.GUARDEX_AUTO_DOCTOR);
896
+ if (explicit != null) {
897
+ return explicit;
898
+ }
899
+ return isInteractiveTerminal();
900
+ }
901
+
902
+ function shouldAutoRunDoctorFromStatus(statusPayload) {
903
+ const repo = statusPayload?.repo || {};
904
+ return Boolean(
905
+ autoDoctorEnabledForCurrentSession()
906
+ && repo.inGitRepo
907
+ && repo.guardexEnabled !== false
908
+ && repo.serviceStatus === 'degraded'
909
+ && repo.scan
910
+ && Number(repo.scan.findings || 0) > 0,
911
+ );
912
+ }
913
+
914
+ function runCliSubprocessWithSpinner(args, options = {}) {
915
+ return new Promise((resolve, reject) => {
916
+ const spinner = options.spinnerMessage
917
+ ? startTransientSpinner(options.spinnerMessage, {
918
+ prefix: options.spinnerPrefix || `[${TOOL_NAME}]`,
919
+ })
920
+ : { stop() {} };
921
+ const child = cp.spawn(process.execPath, [path.resolve(__filename), ...args], {
922
+ cwd: options.cwd || process.cwd(),
923
+ env: {
924
+ ...process.env,
925
+ GUARDEX_AUTO_DOCTOR: '0',
926
+ },
927
+ stdio: ['inherit', 'pipe', 'pipe'],
928
+ });
929
+
930
+ const stopSpinner = () => spinner.stop();
931
+ child.stdout.on('data', (chunk) => {
932
+ stopSpinner();
933
+ process.stdout.write(chunk);
934
+ });
935
+ child.stderr.on('data', (chunk) => {
936
+ stopSpinner();
937
+ process.stderr.write(chunk);
938
+ });
939
+ child.on('error', (error) => {
940
+ stopSpinner();
941
+ reject(error);
942
+ });
943
+ child.on('close', (code) => {
944
+ stopSpinner();
945
+ resolve(typeof code === 'number' ? code : 1);
946
+ });
947
+ });
948
+ }
949
+
950
+ async function maybeAutoRunDoctorFromDefaultStatus(statusPayload) {
951
+ if (!shouldAutoRunDoctorFromStatus(statusPayload)) {
952
+ return false;
953
+ }
954
+
955
+ const target = statusPayload?.repo?.target || process.cwd();
956
+ console.log(`[${TOOL_NAME}] Auto-repair: repo safety is degraded. Running '${SHORT_TOOL_NAME} doctor --current' now.`);
957
+ process.exitCode = await runCliSubprocessWithSpinner(
958
+ ['doctor', '--target', target, '--current'],
959
+ {
960
+ cwd: target,
961
+ spinnerPrefix: `[${TOOL_NAME}] Auto-repair:`,
962
+ spinnerMessage: 'preparing doctor workspace',
963
+ },
964
+ );
965
+ return true;
966
+ }
967
+
892
968
  function parseDotenvAssignmentValue(raw) {
893
969
  let value = String(raw || '').trim();
894
970
  if (!value) return '';
@@ -1672,12 +1748,62 @@ function setExitCodeFromScan(scan) {
1672
1748
  process.exitCode = 0;
1673
1749
  }
1674
1750
 
1675
- function status(rawArgs) {
1676
- const options = parseCommonArgs(rawArgs, {
1677
- target: process.cwd(),
1678
- json: false,
1679
- });
1751
+ function printStatusRepairHint(scanResult) {
1752
+ if (!scanResult || scanResult.guardexEnabled === false) {
1753
+ return;
1754
+ }
1755
+ if (scanResult.errors === 0 && scanResult.warnings === 0) {
1756
+ return;
1757
+ }
1758
+
1759
+ const scanHint = scanResult.errors === 0
1760
+ ? `review warning details with '${SHORT_TOOL_NAME} scan'`
1761
+ : `inspect detailed findings with '${SHORT_TOOL_NAME} scan'`;
1762
+ console.log(
1763
+ `[${TOOL_NAME}] Quick fix: run '${SHORT_TOOL_NAME} doctor' to repair drift, or ${scanHint}.`,
1764
+ );
1765
+ }
1766
+
1767
+ function countAgentWorktrees(repoRoot) {
1768
+ if (!repoRoot || typeof repoRoot !== 'string') return 0;
1769
+ const relPaths = ['.omc/agent-worktrees', '.omx/agent-worktrees'];
1770
+ let count = 0;
1771
+ for (const rel of relPaths) {
1772
+ try {
1773
+ const entries = fs.readdirSync(path.join(repoRoot, rel), { withFileTypes: true });
1774
+ count += entries.filter((entry) => entry.isDirectory()).length;
1775
+ } catch (_err) {
1776
+ // missing dir or permission error; not an active-agent signal
1777
+ }
1778
+ }
1779
+ return count;
1780
+ }
1781
+
1782
+ function deriveNextStepHint({ scanResult, worktreeCount, invoked, inGitRepo }) {
1783
+ if (!inGitRepo) {
1784
+ return `${invoked} setup --target <path-to-git-repo> # initialize guardrails in a repo`;
1785
+ }
1786
+ if (!scanResult) {
1787
+ return `${invoked} setup # bootstrap repo guardrails`;
1788
+ }
1789
+ if (scanResult.guardexEnabled === false) {
1790
+ return `set GUARDEX_ON=1 in .env # re-enable guardrails, then '${invoked} doctor'`;
1791
+ }
1792
+ const branch = scanResult.branch || '';
1793
+ if (branch.startsWith('agent/')) {
1794
+ return `${invoked} branch finish --branch "${branch}" --via-pr --wait-for-merge --cleanup`;
1795
+ }
1796
+ if (worktreeCount > 0) {
1797
+ const plural = worktreeCount === 1 ? 'worktree' : 'worktrees';
1798
+ return `${invoked} finish --all # ${worktreeCount} active agent ${plural}`;
1799
+ }
1800
+ if (scanResult.errors > 0 || scanResult.warnings > 0) {
1801
+ return `${invoked} doctor # repair drift`;
1802
+ }
1803
+ return `${invoked} branch start "<task>" "<agent-name>" # start a sandboxed agent task`;
1804
+ }
1680
1805
 
1806
+ function collectServicesSnapshot() {
1681
1807
  const toolchain = toolchainModule.detectGlobalToolchainPackages();
1682
1808
  const npmServices = GLOBAL_TOOLCHAIN_PACKAGES.map((pkg) => {
1683
1809
  const service = toolchainModule.getGlobalToolchainService(pkg);
@@ -1701,18 +1827,103 @@ function status(rawArgs) {
1701
1827
  const localCompanionServices = toolchainModule.detectOptionalLocalCompanionTools().map((tool) => ({
1702
1828
  name: tool.name,
1703
1829
  displayName: tool.displayName || tool.name,
1830
+ installCommand: tool.installCommand,
1831
+ installArgs: Array.isArray(tool.installArgs) ? [...tool.installArgs] : [],
1704
1832
  status: tool.status,
1705
1833
  }));
1706
1834
  const requiredSystemTools = toolchainModule.detectRequiredSystemTools();
1707
1835
  const services = [
1708
1836
  ...npmServices,
1709
- ...localCompanionServices,
1837
+ ...localCompanionServices.map((tool) => ({
1838
+ name: tool.name,
1839
+ displayName: tool.displayName,
1840
+ status: tool.status,
1841
+ })),
1710
1842
  ...requiredSystemTools.map((tool) => ({
1711
1843
  name: tool.name,
1712
1844
  displayName: tool.displayName || tool.name,
1713
1845
  status: tool.status,
1714
1846
  })),
1715
1847
  ];
1848
+ return { toolchain, npmServices, localCompanionServices, requiredSystemTools, services };
1849
+ }
1850
+
1851
+ function maybePromptInstallMissingCompanions(snapshot) {
1852
+ if (envFlagIsTruthy(process.env.GUARDEX_SKIP_COMPANION_PROMPT)) {
1853
+ return { handled: false, installed: false };
1854
+ }
1855
+ const interactive = Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
1856
+ const autoApproval = toolchainModule.parseAutoApproval('GUARDEX_AUTO_COMPANION_APPROVAL');
1857
+ if (!interactive && autoApproval == null) {
1858
+ return { handled: false, installed: false };
1859
+ }
1860
+ if (!snapshot.toolchain.ok) {
1861
+ return { handled: false, installed: false };
1862
+ }
1863
+
1864
+ const missingPackages = snapshot.npmServices
1865
+ .filter((service) => service.status !== 'active')
1866
+ .map((service) => service.packageName);
1867
+ const missingLocalTools = snapshot.localCompanionServices.filter((tool) => tool.status !== 'active');
1868
+ if (missingPackages.length === 0 && missingLocalTools.length === 0) {
1869
+ return { handled: false, installed: false };
1870
+ }
1871
+
1872
+ const missingNames = [
1873
+ ...missingPackages.map((pkg) => toolchainModule.formatGlobalToolchainServiceName(pkg)),
1874
+ ...missingLocalTools.map((tool) => tool.displayName || tool.name),
1875
+ ];
1876
+ console.log(`[${TOOL_NAME}] Missing companion tools: ${missingNames.join(', ')}.`);
1877
+
1878
+ const promptText = toolchainModule.buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools);
1879
+ const approved = interactive
1880
+ ? toolchainModule.promptYesNoStrict(promptText)
1881
+ : autoApproval;
1882
+
1883
+ if (!approved) {
1884
+ console.log(
1885
+ `[${TOOL_NAME}] Skipped companion install. Set GUARDEX_SKIP_COMPANION_PROMPT=1 to silence this prompt, ` +
1886
+ `or run '${getInvokedCliName()} setup --install-only' later to install manually.`,
1887
+ );
1888
+ return { handled: true, installed: false };
1889
+ }
1890
+
1891
+ const result = toolchainModule.performCompanionInstall(missingPackages, missingLocalTools);
1892
+ if (result.status === 'installed') {
1893
+ console.log(
1894
+ `[${TOOL_NAME}] ✅ Companion tools installed (${(result.packages || []).join(', ')}).`,
1895
+ );
1896
+ return { handled: true, installed: true };
1897
+ }
1898
+ if (result.status === 'failed') {
1899
+ console.log(
1900
+ `[${TOOL_NAME}] ⚠️ Companion install failed: ${result.reason}. ` +
1901
+ `Retry with '${getInvokedCliName()} setup --install-only'.`,
1902
+ );
1903
+ return { handled: true, installed: false };
1904
+ }
1905
+ return { handled: true, installed: false };
1906
+ }
1907
+
1908
+ function status(rawArgs) {
1909
+ const { found: verboseFlag, remaining: afterVerbose } = extractFlag(rawArgs, '--verbose');
1910
+ const options = parseCommonArgs(afterVerbose, {
1911
+ target: process.cwd(),
1912
+ json: false,
1913
+ });
1914
+ const forceCompact = envFlagIsTruthy(process.env.GUARDEX_COMPACT_STATUS);
1915
+ const forceExpand = envFlagIsTruthy(process.env.GUARDEX_VERBOSE_STATUS) || verboseFlag;
1916
+ const interactive = Boolean(process.stdout.isTTY);
1917
+ const invokedBasename = getInvokedCliName();
1918
+
1919
+ let snapshot = collectServicesSnapshot();
1920
+ if (!options.json) {
1921
+ const result = maybePromptInstallMissingCompanions(snapshot);
1922
+ if (result.installed) {
1923
+ snapshot = collectServicesSnapshot();
1924
+ }
1925
+ }
1926
+ let { toolchain, npmServices, localCompanionServices, requiredSystemTools, services } = snapshot;
1716
1927
 
1717
1928
  const targetPath = path.resolve(options.target);
1718
1929
  const inGitRepo = isGitRepo(targetPath);
@@ -1752,18 +1963,27 @@ function status(rawArgs) {
1752
1963
  if (options.json) {
1753
1964
  process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
1754
1965
  process.exitCode = 0;
1755
- return;
1966
+ return payload;
1756
1967
  }
1757
1968
 
1969
+ const allServicesActive = toolchain.ok && services.every((service) => service.status === 'active');
1970
+ const compact = !forceExpand && (forceCompact || (interactive && allServicesActive));
1971
+
1758
1972
  console.log(`[${TOOL_NAME}] CLI: ${payload.cli.runtime}`);
1759
1973
  if (!toolchain.ok) {
1760
1974
  console.log(`[${TOOL_NAME}] ⚠️ Could not detect global services: ${toolchain.error}`);
1761
1975
  }
1762
1976
 
1763
- console.log(`[${TOOL_NAME}] Global services:`);
1764
- for (const service of services) {
1765
- const serviceLabel = service.displayName || service.name;
1766
- console.log(` - ${statusDot(service.status)} ${serviceLabel}: ${service.status}`);
1977
+ if (compact) {
1978
+ console.log(
1979
+ `[${TOOL_NAME}] Global services: ${services.length}/${services.length} ${statusDot('active')} active`,
1980
+ );
1981
+ } else {
1982
+ console.log(`[${TOOL_NAME}] Global services:`);
1983
+ for (const service of services) {
1984
+ const serviceLabel = service.displayName || service.name;
1985
+ console.log(` - ${statusDot(service.status)} ${serviceLabel}: ${service.status}`);
1986
+ }
1767
1987
  }
1768
1988
  const inactiveOptionalCompanions = [...npmServices, ...localCompanionServices]
1769
1989
  .filter((service) => service.status !== 'active')
@@ -1799,8 +2019,16 @@ function status(rawArgs) {
1799
2019
  console.log(
1800
2020
  `[${TOOL_NAME}] Repo safety service: ${statusDot('inactive')} inactive (no git repository at target).`,
1801
2021
  );
2022
+ const inactiveHint = deriveNextStepHint({
2023
+ scanResult: null,
2024
+ worktreeCount: 0,
2025
+ invoked: invokedBasename,
2026
+ inGitRepo,
2027
+ });
2028
+ console.log(`[${TOOL_NAME}] Next: ${inactiveHint}`);
2029
+ printToolLogsSummary({ invokedBasename, compact });
1802
2030
  process.exitCode = 0;
1803
- return;
2031
+ return payload;
1804
2032
  }
1805
2033
 
1806
2034
  if (scanResult.guardexEnabled === false) {
@@ -1809,9 +2037,23 @@ function status(rawArgs) {
1809
2037
  );
1810
2038
  console.log(`[${TOOL_NAME}] Repo: ${scanResult.repoRoot}`);
1811
2039
  console.log(`[${TOOL_NAME}] Branch: ${scanResult.branch}`);
1812
- printToolLogsSummary();
2040
+ const worktreeCountDisabled = countAgentWorktrees(scanResult.repoRoot);
2041
+ if (worktreeCountDisabled > 0) {
2042
+ const plural = worktreeCountDisabled === 1 ? 'worktree' : 'worktrees';
2043
+ console.log(
2044
+ `[${TOOL_NAME}] ⚠ ${worktreeCountDisabled} active agent ${plural} under .omc/agent-worktrees or .omx/agent-worktrees.`,
2045
+ );
2046
+ }
2047
+ const disabledHint = deriveNextStepHint({
2048
+ scanResult,
2049
+ worktreeCount: worktreeCountDisabled,
2050
+ invoked: invokedBasename,
2051
+ inGitRepo,
2052
+ });
2053
+ console.log(`[${TOOL_NAME}] Next: ${disabledHint}`);
2054
+ printToolLogsSummary({ invokedBasename, compact });
1813
2055
  process.exitCode = 0;
1814
- return;
2056
+ return payload;
1815
2057
  }
1816
2058
 
1817
2059
  if (scanResult.errors === 0 && scanResult.warnings === 0) {
@@ -1820,23 +2062,36 @@ function status(rawArgs) {
1820
2062
  console.log(
1821
2063
  `[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.warnings} warning(s)).`,
1822
2064
  );
1823
- console.log(`[${TOOL_NAME}] Run '${TOOL_NAME} scan' to review warning details.`);
1824
2065
  } else if (scanResult.warnings === 0) {
1825
2066
  console.log(
1826
2067
  `[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.errors} error(s)).`,
1827
2068
  );
1828
- console.log(`[${TOOL_NAME}] Run '${TOOL_NAME} scan' for detailed findings.`);
1829
2069
  } else {
1830
2070
  console.log(
1831
2071
  `[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.errors} error(s), ${scanResult.warnings} warning(s)).`,
1832
2072
  );
1833
- console.log(`[${TOOL_NAME}] Run '${TOOL_NAME} scan' for detailed findings.`);
1834
2073
  }
2074
+ printStatusRepairHint(scanResult);
1835
2075
  console.log(`[${TOOL_NAME}] Repo: ${scanResult.repoRoot}`);
1836
2076
  console.log(`[${TOOL_NAME}] Branch: ${scanResult.branch}`);
1837
- printToolLogsSummary();
2077
+ const worktreeCountActive = countAgentWorktrees(scanResult.repoRoot);
2078
+ if (worktreeCountActive > 0) {
2079
+ const plural = worktreeCountActive === 1 ? 'worktree' : 'worktrees';
2080
+ console.log(
2081
+ `[${TOOL_NAME}] ⚠ ${worktreeCountActive} active agent ${plural} → ${invokedBasename} finish --all`,
2082
+ );
2083
+ }
2084
+ const activeHint = deriveNextStepHint({
2085
+ scanResult,
2086
+ worktreeCount: worktreeCountActive,
2087
+ invoked: invokedBasename,
2088
+ inGitRepo,
2089
+ });
2090
+ console.log(`[${TOOL_NAME}] Next: ${activeHint}`);
2091
+ printToolLogsSummary({ invokedBasename, compact });
1838
2092
 
1839
2093
  process.exitCode = 0;
2094
+ return payload;
1840
2095
  }
1841
2096
 
1842
2097
  function install(rawArgs) {
@@ -3246,13 +3501,14 @@ function protect(rawArgs) {
3246
3501
  throw new Error(`Unknown protect subcommand: ${subcommand}`);
3247
3502
  }
3248
3503
 
3249
- function main() {
3504
+ async function main() {
3250
3505
  const args = process.argv.slice(2);
3251
3506
 
3252
3507
  if (args.length === 0) {
3253
3508
  toolchainModule.maybeSelfUpdateBeforeStatus();
3254
3509
  toolchainModule.maybeOpenSpecUpdateBeforeStatus();
3255
- status([]);
3510
+ const statusPayload = status([]);
3511
+ await maybeAutoRunDoctorFromDefaultStatus(statusPayload);
3256
3512
  return;
3257
3513
  }
3258
3514
 
@@ -3322,9 +3578,9 @@ function main() {
3322
3578
  throw new Error(`Unknown command: ${command}`);
3323
3579
  }
3324
3580
 
3325
- function runFromBin() {
3581
+ async function runFromBin() {
3326
3582
  try {
3327
- main();
3583
+ await main();
3328
3584
  } catch (error) {
3329
3585
  console.error(`[${TOOL_NAME}] ${error.message}`);
3330
3586
  process.exitCode = 1;
@@ -3332,7 +3588,7 @@ function runFromBin() {
3332
3588
  }
3333
3589
 
3334
3590
  if (require.main === module) {
3335
- runFromBin();
3591
+ void runFromBin();
3336
3592
  }
3337
3593
 
3338
3594
  module.exports = {
package/src/context.js CHANGED
@@ -363,27 +363,68 @@ const SUGGESTIBLE_COMMANDS = [
363
363
  'print-agents-snippet',
364
364
  'release',
365
365
  ];
366
- const CLI_COMMAND_DESCRIPTIONS = [
367
- ['status', 'Show GitGuardex CLI + service health without modifying files'],
368
- ['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target, --current)'],
369
- ['doctor', 'Repair drift + verify (flags: --target, --current; auto-sandboxes on protected main)'],
370
- ['branch', 'CLI-owned branch workflow surface (start/finish/merge)'],
371
- ['locks', 'CLI-owned file lock surface (claim/allow-delete/release/status/validate)'],
372
- ['worktree', 'CLI-owned worktree cleanup surface (prune)'],
373
- ['hook', 'Hook dispatch/install surface used by managed shims'],
374
- ['migrate', 'Convert legacy repo-local installs to the zero-copy CLI-owned surface'],
375
- ['install-agent-skills', 'Install Guardex Codex/Claude skills into the user home'],
376
- ['protect', 'Manage protected branches (list/add/remove/set/reset)'],
377
- ['merge', 'Create/reuse an integration lane and merge overlapping agent branches'],
378
- ['sync', 'Sync agent branches with origin/<base>'],
379
- ['finish', 'Commit + PR + merge completed agent branches (--all, --branch)'],
380
- ['cleanup', 'Prune merged/stale agent branches and worktrees'],
381
- ['release', 'Create or update the current GitHub release with README-generated notes'],
382
- ['agents', 'Start/stop repo-scoped review + cleanup bots'],
383
- ['prompt', 'Print AI setup checklist or named slices (--exec, --part, --list-parts, --snippet)'],
384
- ['report', 'Security/safety reports (e.g. OpenSSF scorecard, session severity)'],
385
- ['help', 'Show this help output'],
386
- ['version', 'Print GitGuardex version'],
366
+ // CLI_COMMAND_GROUPS is the grouped source of truth the `gx --help` /
367
+ // `gx` no-args renderer uses. Each group is ordered roughly by how often a
368
+ // user reaches for it so the help screen answers "what do I run first?"
369
+ // before "what else can this do?". CLI_COMMAND_DESCRIPTIONS preserves the
370
+ // flat export for callers that still want the ungrouped list.
371
+ const CLI_COMMAND_GROUPS = [
372
+ {
373
+ label: 'Setup & health',
374
+ description: 'Install, repair, and check a repo. Run these first on a new clone.',
375
+ commands: [
376
+ ['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target, --current)'],
377
+ ['doctor', 'Repair drift + verify (flags: --target, --current; auto-sandboxes on protected main)'],
378
+ ['status', 'Show GitGuardex CLI + service health without modifying files'],
379
+ ['migrate', 'Convert legacy repo-local installs to the zero-copy CLI-owned surface'],
380
+ ],
381
+ },
382
+ {
383
+ label: 'Branch workflow',
384
+ description: 'The sandbox commit PR → merge loop for agent-owned branches.',
385
+ commands: [
386
+ ['branch', 'CLI-owned branch workflow surface (start/finish/merge)'],
387
+ ['finish', 'Commit + PR + merge completed agent branches (--all, --branch)'],
388
+ ['merge', 'Create/reuse an integration lane and merge overlapping agent branches'],
389
+ ['sync', 'Sync agent branches with origin/<base>'],
390
+ ['cleanup', 'Prune merged/stale agent branches and worktrees'],
391
+ ],
392
+ },
393
+ {
394
+ label: 'Coordination',
395
+ description: 'File locks, worktrees, hooks, and protected-branch policy.',
396
+ commands: [
397
+ ['locks', 'CLI-owned file lock surface (claim/allow-delete/release/status/validate)'],
398
+ ['worktree', 'CLI-owned worktree cleanup surface (prune)'],
399
+ ['hook', 'Hook dispatch/install surface used by managed shims'],
400
+ ['protect', 'Manage protected branches (list/add/remove/set/reset)'],
401
+ ],
402
+ },
403
+ {
404
+ label: 'Agents & reports',
405
+ description: 'Review / cleanup bots, AI setup prompts, and safety reports.',
406
+ commands: [
407
+ ['agents', 'Start/stop repo-scoped review + cleanup bots'],
408
+ ['install-agent-skills', 'Install Guardex Codex/Claude skills into the user home'],
409
+ ['prompt', 'Print AI setup checklist or named slices (--exec, --part, --list-parts, --snippet)'],
410
+ ['report', 'Security/safety reports (e.g. OpenSSF scorecard, session severity)'],
411
+ ['release', 'Create or update the current GitHub release with README-generated notes'],
412
+ ],
413
+ },
414
+ {
415
+ label: 'Meta',
416
+ description: 'Version + help.',
417
+ commands: [
418
+ ['help', 'Show this help output'],
419
+ ['version', 'Print GitGuardex version'],
420
+ ],
421
+ },
422
+ ];
423
+ const CLI_COMMAND_DESCRIPTIONS = CLI_COMMAND_GROUPS.flatMap((group) => group.commands);
424
+ const CLI_QUICKSTART_STEPS = [
425
+ 'gx setup',
426
+ 'gx branch start "<task>" "<agent>"',
427
+ 'gx branch finish --via-pr --wait-for-merge --cleanup',
387
428
  ];
388
429
  const DEPRECATED_COMMAND_ALIASES = new Map([
389
430
  ['init', { target: 'setup', hint: 'gx setup' }],
@@ -686,6 +727,8 @@ module.exports = {
686
727
  COMMAND_TYPO_ALIASES,
687
728
  SUGGESTIBLE_COMMANDS,
688
729
  CLI_COMMAND_DESCRIPTIONS,
730
+ CLI_COMMAND_GROUPS,
731
+ CLI_QUICKSTART_STEPS,
689
732
  DEPRECATED_COMMAND_ALIASES,
690
733
  AGENT_BOT_DESCRIPTIONS,
691
734
  DOCTOR_AUTO_FINISH_DETAIL_LIMIT,