Skip to content

Commit dd74485

Browse files
NagyViktNagyViktOmX
authored
Make status output compact for agent callers (#518)
Default status output previously expanded the full command catalog whenever stdout was non-TTY, which is exactly how agent tool captures run. Keep the health signal and next action, but reserve the full services list and help tree for --verbose. Constraint: Agent shells capture non-TTY output and pay token cost for every status call Rejected: Hide health details entirely | agents still need service counts and next-step hints Confidence: high Scope-risk: narrow Directive: Keep --verbose as the escape hatch for full service and help details Tested: node --test test/status.test.js test/output.test.js Tested: node -c src/cli/main.js src/output/index.js test/status.test.js Not-tested: full npm test remains blocked by unrelated agents/cockpit harness failures with empty stdout Co-authored-by: NagyVikt <nagy.viktordp@gmail.com> Co-authored-by: OmX <omx@oh-my-codex.dev>
1 parent 76306a5 commit dd74485

3 files changed

Lines changed: 15 additions & 35 deletions

File tree

src/cli/main.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,9 +2026,7 @@ function status(rawArgs) {
20262026
target: process.cwd(),
20272027
json: false,
20282028
});
2029-
const forceCompact = envFlagIsTruthy(process.env.GUARDEX_COMPACT_STATUS);
20302029
const forceExpand = envFlagIsTruthy(process.env.GUARDEX_VERBOSE_STATUS) || verboseFlag;
2031-
const interactive = Boolean(process.stdout.isTTY);
20322030
const invokedBasename = getInvokedCliName();
20332031

20342032
let snapshot = collectServicesSnapshot();
@@ -2081,17 +2079,21 @@ function status(rawArgs) {
20812079
return payload;
20822080
}
20832081

2084-
const allServicesActive = toolchain.ok && services.every((service) => service.status === 'active');
2085-
const compact = !forceExpand && (forceCompact || (interactive && allServicesActive));
2082+
const compact = !forceExpand;
2083+
const activeServiceCount = services.filter((service) => service.status === 'active').length;
2084+
const inactiveServiceCount = services.length - activeServiceCount;
20862085

20872086
console.log(`[${TOOL_NAME}] CLI: ${payload.cli.runtime}`);
20882087
if (!toolchain.ok) {
20892088
console.log(`[${TOOL_NAME}] ⚠️ Could not detect global services: ${toolchain.error}`);
20902089
}
20912090

20922091
if (compact) {
2092+
const serviceSummary = inactiveServiceCount === 0
2093+
? `${activeServiceCount}/${services.length} ${statusDot('active')} active`
2094+
: `${activeServiceCount}/${services.length} ${statusDot('degraded')} active (${inactiveServiceCount} inactive)`;
20932095
console.log(
2094-
`[${TOOL_NAME}] Global services: ${services.length}/${services.length} ${statusDot('active')} active`,
2096+
`[${TOOL_NAME}] Global services: ${serviceSummary}`,
20952097
);
20962098
} else {
20972099
console.log(`[${TOOL_NAME}] Global services:`);

src/output/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ REPO TOGGLE
347347
${repoToggleLines().join('\n')}
348348
349349
NOTES
350-
- No command = ${invoked} status (compact in a TTY; pass --verbose for full services + help tree).
350+
- No command = ${invoked} status (compact by default; pass --verbose for full services + help tree).
351351
- ${invoked} init is an alias of ${invoked} setup.
352352
- Global installs need Y/N approval; GitHub CLI (gh) is required for PR automation.
353353
- Target another repo: ${invoked} <cmd> --target <repo-path>.

test/status.test.js

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -77,30 +77,12 @@ test('default invocation runs non-mutating status output', () => {
7777
const serviceIdx = result.stdout.indexOf('[gitguardex] Repo safety service:');
7878
const repoIdx = result.stdout.indexOf('[gitguardex] Repo:');
7979
const branchIdx = result.stdout.indexOf('[gitguardex] Branch:');
80-
const helpIdx = result.stdout.indexOf('gx help:');
8180
assert.equal(serviceIdx >= 0, true);
8281
assert.equal(repoIdx > serviceIdx, true);
8382
assert.equal(branchIdx > repoIdx, true);
84-
assert.equal(
85-
helpIdx > branchIdx,
86-
true,
87-
`Expected 'gx help:' banner to appear after Branch line.\nstdout=\n${result.stdout}`,
88-
);
89-
assert.match(result.stdout, /gx help:/);
90-
assert.match(result.stdout, /USAGE\n\s+\$ gx <command> \[options\]/);
91-
assert.match(result.stdout, /COMMANDS\n\s+Setup & health/);
92-
assert.match(
93-
result.stdout,
94-
/status\s+Show GitGuardex CLI \+ service health without modifying files/,
95-
);
96-
assert.match(
97-
result.stdout,
98-
/AGENT BOT\n\s+agents\s+Start\/stop review \+ cleanup bots for this repo/,
99-
);
100-
assert.match(
101-
result.stdout,
102-
/REPO TOGGLE\n\s+Set repo-root \.env: GUARDEX_ON=0 disables Guardex, GUARDEX_ON=1 enables it again/,
103-
);
83+
assert.match(result.stdout, /\[gitguardex\] Global services: \d+\/\d+ active/);
84+
assert.doesNotMatch(result.stdout, /gx help:/);
85+
assert.doesNotMatch(result.stdout, /USAGE\n\s+\$ gx <command> \[options\]/);
10486
assert.match(
10587
result.stdout,
10688
/\[gitguardex\] Next:/,
@@ -110,12 +92,10 @@ test('default invocation runs non-mutating status output', () => {
11092
});
11193

11294

113-
test('GUARDEX_COMPACT_STATUS=1 suppresses the full help tree and emits a Next hint', () => {
95+
test('status suppresses the full help tree by default and emits a Next hint', () => {
11496
const repoDir = initRepo();
11597

116-
const result = runNodeWithEnv([], repoDir, {
117-
GUARDEX_COMPACT_STATUS: '1',
118-
});
98+
const result = runNode([], repoDir);
11999
assert.equal(result.status, 0, result.stderr || result.stdout);
120100
assert.match(result.stdout, /\[gitguardex\] CLI:/);
121101
assert.match(
@@ -133,10 +113,8 @@ test('GUARDEX_COMPACT_STATUS=1 suppresses the full help tree and emits a Next hi
133113
test('--verbose forces the expanded services list even when every service is active', () => {
134114
const repoDir = initRepo();
135115

136-
const compactResult = runNodeWithEnv([], repoDir, {
137-
GUARDEX_COMPACT_STATUS: '1',
138-
});
139-
// sanity: compact mode collapses by default when the env flag is set
116+
const compactResult = runNode([], repoDir);
117+
// sanity: compact mode collapses by default
140118
assert.match(compactResult.stdout, /Global services: \d+\/\d+ active/);
141119

142120
const verbose = runNodeWithEnv(['status', '--verbose'], repoDir, {

0 commit comments

Comments
 (0)