Skip to content

Commit 0ed0305

Browse files
NagyViktNagyVikt
andauthored
Reduce noisy Guardex status output for agents (#521)
Agent sessions read gx status frequently, so compact mode should summarize dependency drift without dumping long npm stderr or every inactive companion name. This keeps verbose output available for human diagnosis, keeps RTK as a required command-compression dependency, and preserves the FFF MCP file-search instruction in the multiagent safety template without making fff-mcp a hard system gate. Constraint: Compact status must remain useful as a default agent startup surface Rejected: Make fff-mcp a required system tool | current clients may not expose it, and hard-gating would add noisy failures Confidence: high Scope-risk: narrow Directive: Keep compact status short; put dependency detail behind --verbose Tested: live ghx smoke: 2 repo view requests produced 1 miss, 1 hit, cache size 1 Tested: rtk test node --test test/status.test.js test/setup.test.js test/doctor.test.js test/prompt.test.js Not-tested: repo-wide node --test suite Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
1 parent 5efc609 commit 0ed0305

7 files changed

Lines changed: 79 additions & 35 deletions

File tree

src/cli/main.js

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,7 +2086,10 @@ function status(rawArgs) {
20862086

20872087
console.log(`[${TOOL_NAME}] CLI: ${payload.cli.runtime}`);
20882088
if (!toolchain.ok) {
2089-
console.log(`[${TOOL_NAME}] ⚠️ Could not detect global services: ${toolchain.error}`);
2089+
const detectionError = compact
2090+
? String(toolchain.error || '').split(/\r?\n/).find(Boolean) || 'unknown error'
2091+
: toolchain.error;
2092+
console.log(`[${TOOL_NAME}] ⚠️ Could not detect global services: ${detectionError}`);
20902093
}
20912094

20922095
if (compact) {
@@ -2107,29 +2110,37 @@ function status(rawArgs) {
21072110
.filter((service) => service.status !== 'active')
21082111
.map((service) => service.displayName || service.name);
21092112
if (inactiveOptionalCompanions.length > 0) {
2110-
console.log(
2111-
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.join(', ')}`,
2112-
);
2113-
for (const warning of toolchainModule.describeMissingGlobalDependencyWarnings(
2114-
npmServices
2115-
.filter((service) => service.status === 'inactive')
2116-
.map((service) => service.packageName),
2117-
)) {
2118-
console.log(`[${TOOL_NAME}] ${warning}`);
2113+
if (compact) {
2114+
console.log(
2115+
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.length} (run '${SHORT_TOOL_NAME} setup')`,
2116+
);
2117+
} else {
2118+
console.log(
2119+
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.join(', ')}`,
2120+
);
2121+
for (const warning of toolchainModule.describeMissingGlobalDependencyWarnings(
2122+
npmServices
2123+
.filter((service) => service.status === 'inactive')
2124+
.map((service) => service.packageName),
2125+
)) {
2126+
console.log(`[${TOOL_NAME}] ${warning}`);
2127+
}
2128+
console.log(
2129+
`[${TOOL_NAME}] Run '${SHORT_TOOL_NAME} setup' to install missing companions with an explicit Y/N prompt.`,
2130+
);
21192131
}
2120-
console.log(
2121-
`[${TOOL_NAME}] Run '${SHORT_TOOL_NAME} setup' to install missing companions with an explicit Y/N prompt.`,
2122-
);
21232132
}
21242133
const missingSystemTools = requiredSystemTools.filter((tool) => tool.status !== 'active');
21252134
if (missingSystemTools.length > 0) {
21262135
const tools = missingSystemTools
21272136
.map((tool) => tool.displayName || tool.name)
21282137
.join(', ');
21292138
console.log(`[${TOOL_NAME}] ⚠️ Missing required system tool(s): ${tools}`);
2130-
for (const tool of missingSystemTools) {
2131-
const reasonText = tool.reason ? ` (${tool.reason})` : '';
2132-
console.log(` - install ${tool.name}: ${tool.installHint}${reasonText}`);
2139+
if (!compact) {
2140+
for (const tool of missingSystemTools) {
2141+
const reasonText = tool.reason ? ` (${tool.reason})` : '';
2142+
console.log(` - install ${tool.name}: ${tool.installHint}${reasonText}`);
2143+
}
21332144
}
21342145
}
21352146

src/context.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ function resolveGithubCliBin(env = process.env) {
7676

7777
const GH_BIN = resolveGithubCliBin();
7878
const RTK_BIN = process.env.GUARDEX_RTK_BIN || 'rtk';
79-
const FFF_MCP_BIN = process.env.GUARDEX_FFF_MCP_BIN || 'fff-mcp';
8079
const REQUIRED_SYSTEM_TOOLS = [
8180
{
8281
name: 'gh',
@@ -90,12 +89,6 @@ const REQUIRED_SYSTEM_TOOLS = [
9089
command: RTK_BIN,
9190
installHint: 'Install RTK and ensure `rtk` is on PATH.',
9291
},
93-
{
94-
name: 'fff-mcp',
95-
displayName: 'FFF MCP (fff-mcp)',
96-
command: FFF_MCP_BIN,
97-
installHint: 'https://github.com/dmtrKovalenko/fff.nvim',
98-
},
9992
];
10093
const MAINTAINER_RELEASE_REPO = path.resolve(
10194
process.env.GUARDEX_RELEASE_REPO || PACKAGE_ROOT,

templates/AGENTS.multiagent-safety.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ When `rtk` is available, prefer it for noisy shell discovery and verification.
152152

153153
Use the fff MCP tools for all file search operations instead of default tools.
154154

155-
If `fff-mcp` is missing, run `gx setup` or `gx doctor` and follow the required system tool warning before falling back to repo-local search.
155+
If fff MCP tools are unavailable in the current client, fall back to `rtk grep`, `rtk ls`, or `rg` and keep output compact.
156156

157157
### Caveman style
158158

test/doctor.test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,12 @@ test('doctor warns when required system tool dependencies are missing', () => {
6868

6969
const result = runNodeWithEnv(['doctor', '--target', repoDir], repoDir, {
7070
GUARDEX_RTK_BIN: 'rtk-command-not-found-for-test',
71-
GUARDEX_FFF_MCP_BIN: 'fff-mcp-command-not-found-for-test',
7271
});
7372

7473
assert.equal(result.status, 0, result.stderr || result.stdout);
75-
assert.match(result.stdout, /Missing required system tool\(s\): rtk, fff-mcp/);
74+
assert.match(result.stdout, /Missing required system tool\(s\): rtk/);
7675
assert.match(result.stdout, /Install rtk: Install RTK and ensure `rtk` is on PATH\./);
77-
assert.match(result.stdout, /Install fff-mcp: https:\/\/github\.com\/dmtrKovalenko\/fff\.nvim/);
76+
assert.doesNotMatch(result.stdout, /fff-mcp/);
7877
});
7978

8079
test('doctor --force <managed-path> rewrites only the named managed shim', () => {

test/prompt.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ test('prompt --snippet prints the managed AGENTS template with token budget and
166166
assert.match(result.stdout, /Do not wrap machine-readable commands with RTK/);
167167
assert.match(result.stdout, /### FFF file search/);
168168
assert.match(result.stdout, /Use the fff MCP tools for all file search operations instead of default tools\./);
169+
assert.match(result.stdout, /If fff MCP tools are unavailable in the current client/);
169170
assert.match(result.stdout, /### Caveman style/);
170171
assert.match(result.stdout, /Answer order stays fixed: answer first, cause next, fix or next step last\./);
171172
});

test/setup.test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,14 +1433,13 @@ exit 1
14331433
GUARDEX_HOME_DIR: fakeHome,
14341434
GUARDEX_GH_BIN: 'gh-command-not-found-for-test',
14351435
GUARDEX_RTK_BIN: 'rtk-command-not-found-for-test',
1436-
GUARDEX_FFF_MCP_BIN: 'fff-mcp-command-not-found-for-test',
14371436
});
14381437

14391438
assert.equal(result.status, 0, result.stderr || result.stdout);
1440-
assert.match(result.stdout, /Missing required system tool\(s\): gh, rtk, fff-mcp/);
1439+
assert.match(result.stdout, /Missing required system tool\(s\): gh, rtk/);
14411440
assert.match(result.stdout, /https:\/\/cli\.github\.com\//);
14421441
assert.match(result.stdout, /Install rtk: Install RTK and ensure `rtk` is on PATH\./);
1443-
assert.match(result.stdout, /Install fff-mcp: https:\/\/github\.com\/dmtrKovalenko\/fff\.nvim/);
1442+
assert.doesNotMatch(result.stdout, /fff-mcp/);
14441443
});
14451444

14461445
});

test/status.test.js

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,24 @@ test('status suppresses the full help tree by default and emits a Next hint', ()
110110
});
111111

112112

113+
test('compact status prints only the first global-service detection error line', () => {
114+
const repoDir = initRepo();
115+
const fakeNpm = createFakeNpmScript(`
116+
echo "first npm failure line" >&2
117+
echo "second npm failure line" >&2
118+
exit 1
119+
`);
120+
121+
const result = runNodeWithEnv(['status', '--target', repoDir], repoDir, {
122+
GUARDEX_NPM_BIN: fakeNpm,
123+
});
124+
125+
assert.equal(result.status, 0, result.stderr || result.stdout);
126+
assert.match(result.stdout, /Could not detect global services: first npm failure line/);
127+
assert.doesNotMatch(result.stdout, /second npm failure line/);
128+
});
129+
130+
113131
test('--verbose forces the expanded services list even when every service is active', () => {
114132
const repoDir = initRepo();
115133

@@ -523,6 +541,32 @@ exit 1
523541
});
524542

525543

544+
test('compact status summarizes inactive optional companions without dependency detail', () => {
545+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'guardex-status-target-'));
546+
const fakeHome = createGuardexCompanionHome({ cavekit: true, caveman: true });
547+
const fakeNpm = createFakeNpmScript(`
548+
if [[ "$1" == "list" ]]; then
549+
cat <<'JSON'
550+
{"dependencies":{"oh-my-codex":{"version":"1.0.0"},"@fission-ai/openspec":{"version":"1.0.0"},"@imdeadpool/colony-cli":{"version":"1.0.0"},"@imdeadpool/codex-account-switcher":{"version":"1.0.0"}}}
551+
JSON
552+
exit 0
553+
fi
554+
echo "unexpected npm args: $*" >&2
555+
exit 1
556+
`);
557+
558+
const result = runNodeWithEnv(['status', '--target', targetDir], targetDir, {
559+
GUARDEX_NPM_BIN: fakeNpm,
560+
GUARDEX_HOME_DIR: fakeHome,
561+
});
562+
563+
assert.equal(result.status, 0, result.stderr || result.stdout);
564+
assert.match(result.stdout, /Optional companion tools inactive: 1 \(run 'gx setup'\)/);
565+
assert.doesNotMatch(result.stdout, /oh-my-claudecode: inactive/);
566+
assert.doesNotMatch(result.stdout, /Yeachan-Heo\/oh-my-claudecode/);
567+
});
568+
569+
526570
test('status detects local cavekit and caveman companion installs', () => {
527571
const repoDir = initRepo();
528572
const fakeHome = createGuardexCompanionHome({ cavekit: true, caveman: true });
@@ -562,21 +606,18 @@ test('status reports gh dependency as inactive when gh is unavailable', () => {
562606
assert.equal(ghService.status, 'inactive');
563607
});
564608

565-
test('status reports rtk and fff-mcp dependencies as inactive when unavailable', () => {
609+
test('status reports rtk dependency as inactive when unavailable', () => {
566610
const repoDir = initRepo();
567611
const result = runNodeWithEnv(['status', '--target', repoDir, '--json'], repoDir, {
568612
GUARDEX_RTK_BIN: 'rtk-command-not-found-for-test',
569-
GUARDEX_FFF_MCP_BIN: 'fff-mcp-command-not-found-for-test',
570613
});
571614

572615
assert.equal(result.status, 0, result.stderr || result.stdout);
573616
const payload = JSON.parse(result.stdout);
574617
const rtkService = payload.services.find((service) => service.name === 'rtk');
575-
const fffService = payload.services.find((service) => service.name === 'fff-mcp');
576618
assert.ok(rtkService, 'rtk service should be included in status payload');
577-
assert.ok(fffService, 'fff-mcp service should be included in status payload');
578619
assert.equal(rtkService.status, 'inactive');
579-
assert.equal(fffService.status, 'inactive');
620+
assert.equal(payload.services.some((service) => service.name === 'fff-mcp'), false);
580621
});
581622

582623

0 commit comments

Comments
 (0)