Skip to content

Commit ef5321e

Browse files
committed
fix(doctor): handle Claude Code plugin list without blank separators
1 parent f4bab76 commit ef5321e

3 files changed

Lines changed: 47 additions & 4 deletions

File tree

dist/bin/cc-safety-net.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5206,8 +5206,16 @@ function detectClaudeCode(pluginListOutput) {
52065206
};
52075207
}
52085208
function _findClaudeSafetyNetPluginBlock(output) {
5209-
const pluginLinePattern = new RegExp(`^\\s*(?:\\S+\\s+)?${_escapeRegExp(CLAUDE_SAFETY_NET_PLUGIN_ID)}\\s*$`, "m");
5210-
return output.split(/\n\s*\n/).find((block) => pluginLinePattern.test(block));
5209+
const pluginLinePattern = new RegExp(`^\\s*(?:\\S+\\s+)?${_escapeRegExp(CLAUDE_SAFETY_NET_PLUGIN_ID)}\\s*$`);
5210+
const pluginStartPattern = /^\s*(?:\S+\s+)?\S+@\S+\s*$/;
5211+
const lines = output.split(`
5212+
`);
5213+
const startIndex = lines.findIndex((line) => pluginLinePattern.test(line));
5214+
if (startIndex === -1)
5215+
return;
5216+
const endIndex = lines.findIndex((line, index) => index > startIndex && pluginStartPattern.test(line));
5217+
return lines.slice(startIndex, endIndex === -1 ? undefined : endIndex).join(`
5218+
`);
52115219
}
52125220
function _escapeRegExp(value) {
52135221
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

src/bin/doctor/hooks.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,17 @@ function detectClaudeCode(pluginListOutput: string | null | undefined): HookStat
265265
function _findClaudeSafetyNetPluginBlock(output: string): string | undefined {
266266
const pluginLinePattern = new RegExp(
267267
`^\\s*(?:\\S+\\s+)?${_escapeRegExp(CLAUDE_SAFETY_NET_PLUGIN_ID)}\\s*$`,
268-
'm',
269268
);
269+
const pluginStartPattern = /^\s*(?:\S+\s+)?\S+@\S+\s*$/;
270+
const lines = output.split('\n');
271+
const startIndex = lines.findIndex((line) => pluginLinePattern.test(line));
270272

271-
return output.split(/\n\s*\n/).find((block) => pluginLinePattern.test(block));
273+
if (startIndex === -1) return undefined;
274+
275+
const endIndex = lines.findIndex(
276+
(line, index) => index > startIndex && pluginStartPattern.test(line),
277+
);
278+
return lines.slice(startIndex, endIndex === -1 ? undefined : endIndex).join('\n');
272279
}
273280

274281
function _escapeRegExp(value: string): string {

tests/bin/doctor/hooks.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,34 @@ describe('detectAllHooks', () => {
199199
}
200200
});
201201

202+
test('Claude Code: reads status from safety-net entry without blank separators', () => {
203+
const tmpBase = join(tmpdir(), `doctor-hooks-${Date.now()}`);
204+
const homeDir = join(tmpBase, 'home');
205+
const projectDir = join(tmpBase, 'project');
206+
mkdirSync(homeDir, { recursive: true });
207+
mkdirSync(projectDir, { recursive: true });
208+
209+
try {
210+
const hooks = detectAllHooks(projectDir, {
211+
homeDir,
212+
claudePluginListOutput: `Installed plugins:
213+
❯ code-simplifier@claude-plugins-official
214+
Version: 1.0.0
215+
Scope: user
216+
Status: ✘ disabled
217+
❯ safety-net@cc-marketplace
218+
Version: 0.8.2
219+
Scope: user
220+
Status: ✔ enabled`,
221+
});
222+
const claude = hooks.find((hook) => hook.platform === 'claude-code');
223+
expect(claude?.status).toBe('configured');
224+
expect(claude?.method).toBe('plugin list');
225+
} finally {
226+
rmSync(tmpBase, { recursive: true, force: true });
227+
}
228+
});
229+
202230
test('Claude Code: n/a when plugin list is unavailable', () => {
203231
const tmpBase = join(tmpdir(), `doctor-hooks-${Date.now()}`);
204232
const homeDir = join(tmpBase, 'home');

0 commit comments

Comments
 (0)