(_toolCallId, params, signal, onUpdate, ctx)
| 92 | ], |
| 93 | parameters: scanSchema, |
| 94 | async execute(_toolCallId, params, signal, onUpdate, ctx) { |
| 95 | const bin = findSkillSpectorBin(); |
| 96 | const args = buildScanArgs(params, ctx.cwd); |
| 97 | const env: Record<string, string> = {}; |
| 98 | |
| 99 | if (params.provider) env.SKILLSPECTOR_PROVIDER = params.provider; |
| 100 | if (params.model) env.SKILLSPECTOR_MODEL = params.model; |
| 101 | |
| 102 | onUpdate?.({ content: [{ type: "text", text: `Running ${bin} ${args.slice(0, 2).join(" ")} ...` }] }); |
| 103 | |
| 104 | const result = await pi.exec(bin, args, { |
| 105 | cwd: ctx.cwd, |
| 106 | env, |
| 107 | signal, |
| 108 | timeout: 120000, |
| 109 | }); |
| 110 | |
| 111 | const stdout = truncateText(redactSecrets(result.stdout ?? "")); |
| 112 | const stderr = truncateText(redactSecrets(result.stderr ?? ""), 6000); |
| 113 | |
| 114 | if (result.code !== 0) { |
| 115 | throw new Error(`SkillSpector failed with exit code ${result.code}.\n${stderr.text}`); |
| 116 | } |
| 117 | |
| 118 | const outputPath = resolveMaybePath(ctx.cwd, params.output); |
| 119 | const lines = [ |
| 120 | `SkillSpector scan complete: ${params.target}`, |
| 121 | `format: ${params.format ?? "terminal"}`, |
| 122 | `noLlm: ${params.noLlm ?? true}`, |
| 123 | ]; |
| 124 | if (outputPath) lines.push(`output: ${outputPath}`); |
| 125 | if (stdout.truncated) lines.push("stdout truncated: true"); |
| 126 | if (stderr.text.trim()) lines.push(`stderr:\n${stderr.text}`); |
| 127 | if (stdout.text.trim()) lines.push(`stdout:\n${stdout.text}`); |
| 128 | |
| 129 | return { |
| 130 | content: [{ type: "text", text: lines.join("\n") }], |
| 131 | details: { |
| 132 | code: result.code, |
| 133 | command: bin, |
| 134 | args, |
| 135 | outputPath, |
| 136 | stdoutTruncated: stdout.truncated, |
| 137 | stderrTruncated: stderr.truncated, |
| 138 | }, |
| 139 | }; |
| 140 | }, |
| 141 | }); |
| 142 | } |
nothing calls this directly
no test coverage detected