codegate-ai 0.4.0 → 0.6.0
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 +63 -9
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +115 -31
- package/dist/commands/clawhub-wrapper.d.ts +76 -0
- package/dist/commands/clawhub-wrapper.js +631 -0
- package/dist/commands/scan-command.d.ts +9 -0
- package/dist/commands/scan-command.js +228 -204
- package/dist/commands/skills-wrapper.d.ts +10 -1
- package/dist/commands/skills-wrapper.js +35 -16
- package/package.json +1 -1
|
@@ -68,229 +68,249 @@ function deepAgentOptions(report, config) {
|
|
|
68
68
|
return order.indexOf(left.id) - order.indexOf(right.id);
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
export async function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
report
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
export async function runScanAnalysis(input, deps) {
|
|
72
|
+
const interactivePromptsEnabled = deps.isTTY() && input.options.noTui !== true;
|
|
73
|
+
const discoveryContext = deps.prepareScanDiscovery
|
|
74
|
+
? await deps.prepareScanDiscovery(input.scanTarget, input.config, {
|
|
75
|
+
explicitCandidates: input.explicitCandidates,
|
|
76
|
+
})
|
|
77
|
+
: undefined;
|
|
78
|
+
let report = await deps.runScan({
|
|
79
|
+
version: input.version,
|
|
80
|
+
scanTarget: input.scanTarget,
|
|
81
|
+
config: input.config,
|
|
82
|
+
flags: input.options,
|
|
83
|
+
discoveryContext,
|
|
84
|
+
});
|
|
85
|
+
if (input.displayTarget && input.displayTarget !== report.scan_target) {
|
|
86
|
+
report = {
|
|
87
|
+
...report,
|
|
88
|
+
scan_target: input.displayTarget,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const deepScanNotes = [];
|
|
92
|
+
if (input.options.deep) {
|
|
93
|
+
const discoverResources = deps.discoverDeepResources ?? (async () => []);
|
|
94
|
+
const discoverLocalTextTargets = deps.discoverLocalTextTargets ?? (async () => []);
|
|
95
|
+
const resources = await discoverResources(input.scanTarget, input.config, discoveryContext);
|
|
96
|
+
const localTextTargets = await discoverLocalTextTargets(input.scanTarget, input.config, discoveryContext);
|
|
97
|
+
const optionsForAgent = deepAgentOptions(report, input.config);
|
|
98
|
+
let selectedAgent = null;
|
|
99
|
+
if ((resources.length > 0 || localTextTargets.length > 0) && optionsForAgent.length > 0) {
|
|
100
|
+
if (input.options.force || !interactivePromptsEnabled) {
|
|
101
|
+
selectedAgent = optionsForAgent[0] ?? null;
|
|
102
|
+
}
|
|
103
|
+
else if (deps.requestDeepAgentSelection) {
|
|
104
|
+
selectedAgent = await deps.requestDeepAgentSelection(optionsForAgent);
|
|
105
|
+
}
|
|
91
106
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const optionsForAgent = deepAgentOptions(report, input.config);
|
|
99
|
-
let selectedAgent = null;
|
|
100
|
-
if ((resources.length > 0 || localTextTargets.length > 0) && optionsForAgent.length > 0) {
|
|
101
|
-
if (input.options.force || !interactivePromptsEnabled) {
|
|
102
|
-
selectedAgent = optionsForAgent[0] ?? null;
|
|
103
|
-
}
|
|
104
|
-
else if (deps.requestDeepAgentSelection) {
|
|
105
|
-
selectedAgent = await deps.requestDeepAgentSelection(optionsForAgent);
|
|
106
|
-
}
|
|
107
|
+
if (resources.length === 0 && localTextTargets.length === 0) {
|
|
108
|
+
deepScanNotes.push(...noEligibleDeepResourceNotes());
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
if (selectedAgent) {
|
|
112
|
+
deepScanNotes.push(`Deep scan meta-agent selected: ${selectedAgent.label} (${selectedAgent.binary})`);
|
|
107
113
|
}
|
|
108
|
-
if (
|
|
109
|
-
deepScanNotes.push(
|
|
114
|
+
else if (optionsForAgent.length > 0) {
|
|
115
|
+
deepScanNotes.push("Deep scan meta-agent skipped. Running deterministic Layer 3 checks only.");
|
|
110
116
|
}
|
|
111
117
|
else {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
deepScanNotes.push("No supported deep-scan agent detected (Claude Code, Codex CLI, or OpenCode). Running deterministic Layer 3 checks only.");
|
|
118
|
+
deepScanNotes.push("No supported deep-scan agent detected (Claude Code, Codex CLI, or OpenCode). Running deterministic Layer 3 checks only.");
|
|
119
|
+
}
|
|
120
|
+
if (resources.length > 0) {
|
|
121
|
+
if (!deps.executeDeepResource) {
|
|
122
|
+
throw new Error("Deep resource executor not configured");
|
|
120
123
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
let resourcesWithFetchedMetadata = 0;
|
|
125
|
+
let executedMetaAgentCommands = 0;
|
|
126
|
+
const outcomes = await runDeepScanWithConsent(resources, async (resource) => {
|
|
127
|
+
if (input.options.force) {
|
|
128
|
+
return true;
|
|
124
129
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
130
|
+
if (deps.requestDeepScanConsent) {
|
|
131
|
+
return await deps.requestDeepScanConsent(resource);
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}, async (resource) => {
|
|
135
|
+
const fetched = await deps.executeDeepResource(resource);
|
|
136
|
+
if (fetched.status !== "ok" || !selectedAgent) {
|
|
137
|
+
return fetched;
|
|
138
|
+
}
|
|
139
|
+
resourcesWithFetchedMetadata += 1;
|
|
140
|
+
const prompt = buildSecurityAnalysisPrompt({
|
|
141
|
+
resourceId: resource.id,
|
|
142
|
+
resourceSummary: metadataSummary(fetched.metadata),
|
|
143
|
+
});
|
|
144
|
+
const command = buildMetaAgentCommand({
|
|
145
|
+
tool: selectedAgent.metaTool,
|
|
146
|
+
prompt,
|
|
147
|
+
workingDirectory: input.scanTarget,
|
|
148
|
+
binaryPath: selectedAgent.binary,
|
|
149
|
+
});
|
|
150
|
+
const commandContext = {
|
|
151
|
+
resource,
|
|
152
|
+
agent: selectedAgent,
|
|
153
|
+
command,
|
|
154
|
+
};
|
|
155
|
+
const approvedCommand = input.options.force ||
|
|
156
|
+
(deps.requestMetaAgentCommandConsent
|
|
157
|
+
? await deps.requestMetaAgentCommandConsent(commandContext)
|
|
158
|
+
: false);
|
|
159
|
+
if (!approvedCommand) {
|
|
160
|
+
return {
|
|
161
|
+
...fetched,
|
|
162
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
163
|
+
id: `layer3-meta-agent-skipped-${resource.id}`,
|
|
164
|
+
severity: "INFO",
|
|
165
|
+
description: `Deep scan meta-agent command skipped for ${resource.id}`,
|
|
166
|
+
}),
|
|
155
167
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
164
|
-
id: `layer3-meta-agent-skipped-${resource.id}`,
|
|
165
|
-
severity: "INFO",
|
|
166
|
-
description: `Deep scan meta-agent command skipped for ${resource.id}`,
|
|
167
|
-
}),
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
if (!deps.runMetaAgentCommand) {
|
|
171
|
-
throw new Error("Meta-agent command runner not configured");
|
|
172
|
-
}
|
|
173
|
-
executedMetaAgentCommands += 1;
|
|
174
|
-
const commandResult = await deps.runMetaAgentCommand(commandContext);
|
|
175
|
-
if (commandResult.code !== 0) {
|
|
176
|
-
return {
|
|
177
|
-
...fetched,
|
|
178
|
-
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
179
|
-
id: `layer3-meta-agent-command-error-${resource.id}`,
|
|
180
|
-
severity: "LOW",
|
|
181
|
-
description: `Deep scan meta-agent command failed for ${resource.id}`,
|
|
182
|
-
evidence: commandResult.stderr || `exit code: ${commandResult.code}`,
|
|
183
|
-
}),
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
const parsedOutput = parseMetaAgentOutput(commandResult.stdout);
|
|
187
|
-
if (parsedOutput === null) {
|
|
188
|
-
return {
|
|
189
|
-
...fetched,
|
|
190
|
-
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
191
|
-
id: `layer3-meta-agent-parse-error-${resource.id}`,
|
|
192
|
-
severity: "LOW",
|
|
193
|
-
description: `Deep scan meta-agent output was not valid JSON for ${resource.id}`,
|
|
194
|
-
evidence: commandResult.stdout.slice(0, 400),
|
|
195
|
-
}),
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
const normalizedOutput = Array.isArray(parsedOutput)
|
|
199
|
-
? { findings: parsedOutput }
|
|
200
|
-
: parsedOutput;
|
|
168
|
+
}
|
|
169
|
+
if (!deps.runMetaAgentCommand) {
|
|
170
|
+
throw new Error("Meta-agent command runner not configured");
|
|
171
|
+
}
|
|
172
|
+
executedMetaAgentCommands += 1;
|
|
173
|
+
const commandResult = await deps.runMetaAgentCommand(commandContext);
|
|
174
|
+
if (commandResult.code !== 0) {
|
|
201
175
|
return {
|
|
202
176
|
...fetched,
|
|
203
|
-
metadata:
|
|
177
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
178
|
+
id: `layer3-meta-agent-command-error-${resource.id}`,
|
|
179
|
+
severity: "LOW",
|
|
180
|
+
description: `Deep scan meta-agent command failed for ${resource.id}`,
|
|
181
|
+
evidence: commandResult.stderr || `exit code: ${commandResult.code}`,
|
|
182
|
+
}),
|
|
204
183
|
};
|
|
205
|
-
});
|
|
206
|
-
const layer3Findings = layer3OutcomesToFindings(outcomes, {
|
|
207
|
-
unicodeAnalysis: input.config.unicode_analysis,
|
|
208
|
-
});
|
|
209
|
-
report = mergeLayer3Findings(report, layer3Findings);
|
|
210
|
-
if (selectedAgent) {
|
|
211
|
-
if (resourcesWithFetchedMetadata === 0) {
|
|
212
|
-
deepScanNotes.push("Selected meta-agent was not executed because no approved resources returned metadata successfully.");
|
|
213
|
-
}
|
|
214
|
-
else if (executedMetaAgentCommands === 0) {
|
|
215
|
-
deepScanNotes.push("Selected meta-agent was not executed because meta-agent command execution was not approved.");
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
const suffix = executedMetaAgentCommands === 1 ? "" : "s";
|
|
219
|
-
deepScanNotes.push(`Deep scan meta-agent executed for ${executedMetaAgentCommands} resource${suffix}.`);
|
|
220
|
-
}
|
|
221
184
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
185
|
+
const parsedOutput = parseMetaAgentOutput(commandResult.stdout);
|
|
186
|
+
if (parsedOutput === null) {
|
|
187
|
+
return {
|
|
188
|
+
...fetched,
|
|
189
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
190
|
+
id: `layer3-meta-agent-parse-error-${resource.id}`,
|
|
191
|
+
severity: "LOW",
|
|
192
|
+
description: `Deep scan meta-agent output was not valid JSON for ${resource.id}`,
|
|
193
|
+
evidence: commandResult.stdout.slice(0, 400),
|
|
194
|
+
}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const normalizedOutput = Array.isArray(parsedOutput)
|
|
198
|
+
? { findings: parsedOutput }
|
|
199
|
+
: parsedOutput;
|
|
200
|
+
return {
|
|
201
|
+
...fetched,
|
|
202
|
+
metadata: mergeMetaAgentMetadata(fetched.metadata, normalizedOutput),
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
const layer3Findings = layer3OutcomesToFindings(outcomes, {
|
|
206
|
+
unicodeAnalysis: input.config.unicode_analysis,
|
|
207
|
+
});
|
|
208
|
+
report = mergeLayer3Findings(report, layer3Findings);
|
|
209
|
+
if (selectedAgent) {
|
|
210
|
+
if (resourcesWithFetchedMetadata === 0) {
|
|
211
|
+
deepScanNotes.push("Selected meta-agent was not executed because no approved resources returned metadata successfully.");
|
|
226
212
|
}
|
|
227
|
-
else if (
|
|
228
|
-
deepScanNotes.push("
|
|
213
|
+
else if (executedMetaAgentCommands === 0) {
|
|
214
|
+
deepScanNotes.push("Selected meta-agent was not executed because meta-agent command execution was not approved.");
|
|
229
215
|
}
|
|
230
216
|
else {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
? { findings: parsedOutput }
|
|
276
|
-
: parsedOutput;
|
|
277
|
-
const localFindings = parseLocalTextFindings(target.reportPath, normalizedOutput);
|
|
278
|
-
report = mergeLayer3Findings(report, localFindings);
|
|
217
|
+
const suffix = executedMetaAgentCommands === 1 ? "" : "s";
|
|
218
|
+
deepScanNotes.push(`Deep scan meta-agent executed for ${executedMetaAgentCommands} resource${suffix}.`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (localTextTargets.length > 0) {
|
|
223
|
+
if (!selectedAgent) {
|
|
224
|
+
deepScanNotes.push("Local instruction-file analysis skipped because no meta-agent was selected.");
|
|
225
|
+
}
|
|
226
|
+
else if (!supportsToollessLocalTextAnalysis(selectedAgent.metaTool)) {
|
|
227
|
+
deepScanNotes.push("Local instruction-file analysis was skipped because the selected agent does not support tool-less analysis.");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Local instruction files are analyzed as inert text only; referenced URLs stay as evidence, not inputs.
|
|
231
|
+
if (!deps.runMetaAgentCommand) {
|
|
232
|
+
throw new Error("Meta-agent command runner not configured");
|
|
233
|
+
}
|
|
234
|
+
const isolatedWorkingDirectory = mkdtempSync(join(tmpdir(), "codegate-local-analysis-"));
|
|
235
|
+
let executedLocalAnalyses = 0;
|
|
236
|
+
try {
|
|
237
|
+
for (const target of localTextTargets) {
|
|
238
|
+
const prompt = buildLocalTextAnalysisPrompt({
|
|
239
|
+
filePath: target.reportPath,
|
|
240
|
+
textContent: buildPromptEvidenceText(target.textContent),
|
|
241
|
+
referencedUrls: target.referencedUrls,
|
|
242
|
+
});
|
|
243
|
+
const command = buildMetaAgentCommand({
|
|
244
|
+
tool: selectedAgent.metaTool,
|
|
245
|
+
prompt,
|
|
246
|
+
workingDirectory: isolatedWorkingDirectory,
|
|
247
|
+
binaryPath: selectedAgent.binary,
|
|
248
|
+
});
|
|
249
|
+
command.timeoutMs = 60_000;
|
|
250
|
+
const commandContext = {
|
|
251
|
+
localFile: target,
|
|
252
|
+
agent: selectedAgent,
|
|
253
|
+
command,
|
|
254
|
+
};
|
|
255
|
+
const approvedCommand = input.options.force ||
|
|
256
|
+
(deps.requestMetaAgentCommandConsent
|
|
257
|
+
? await deps.requestMetaAgentCommandConsent(commandContext)
|
|
258
|
+
: false);
|
|
259
|
+
if (!approvedCommand) {
|
|
260
|
+
continue;
|
|
279
261
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
262
|
+
executedLocalAnalyses += 1;
|
|
263
|
+
const commandResult = await deps.runMetaAgentCommand(commandContext);
|
|
264
|
+
if (commandResult.code !== 0) {
|
|
265
|
+
deepScanNotes.push(`Local instruction-file analysis failed for ${target.reportPath}: ${commandResult.stderr || `exit code: ${commandResult.code}`}`);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const parsedOutput = parseMetaAgentOutput(commandResult.stdout);
|
|
269
|
+
if (parsedOutput === null) {
|
|
270
|
+
deepScanNotes.push(`Local instruction-file analysis returned invalid JSON for ${target.reportPath}.`);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const normalizedOutput = Array.isArray(parsedOutput)
|
|
274
|
+
? { findings: parsedOutput }
|
|
275
|
+
: parsedOutput;
|
|
276
|
+
const localFindings = parseLocalTextFindings(target.reportPath, normalizedOutput);
|
|
277
|
+
report = mergeLayer3Findings(report, localFindings);
|
|
287
278
|
}
|
|
288
279
|
}
|
|
280
|
+
finally {
|
|
281
|
+
rmSync(isolatedWorkingDirectory, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
if (executedLocalAnalyses > 0) {
|
|
284
|
+
const suffix = executedLocalAnalyses === 1 ? "" : "s";
|
|
285
|
+
deepScanNotes.push(`Local instruction-file analysis executed for ${executedLocalAnalyses} file${suffix}.`);
|
|
286
|
+
}
|
|
289
287
|
}
|
|
290
288
|
}
|
|
291
289
|
}
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
}
|
|
291
|
+
report = applyConfigPolicy(report, input.config);
|
|
292
|
+
report = reorderRequestedTargetFindings(report, input.displayTarget);
|
|
293
|
+
return {
|
|
294
|
+
report,
|
|
295
|
+
deepScanNotes,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
export async function executeScanCommand(input, deps) {
|
|
299
|
+
try {
|
|
300
|
+
const interactivePromptsEnabled = deps.isTTY() && input.options.noTui !== true;
|
|
301
|
+
const { report: analyzedReport, deepScanNotes } = await runScanAnalysis(input, {
|
|
302
|
+
isTTY: deps.isTTY,
|
|
303
|
+
runScan: deps.runScan,
|
|
304
|
+
prepareScanDiscovery: deps.prepareScanDiscovery,
|
|
305
|
+
discoverDeepResources: deps.discoverDeepResources,
|
|
306
|
+
discoverLocalTextTargets: deps.discoverLocalTextTargets,
|
|
307
|
+
requestDeepScanConsent: deps.requestDeepScanConsent,
|
|
308
|
+
requestDeepAgentSelection: deps.requestDeepAgentSelection,
|
|
309
|
+
requestMetaAgentCommandConsent: deps.requestMetaAgentCommandConsent,
|
|
310
|
+
runMetaAgentCommand: deps.runMetaAgentCommand,
|
|
311
|
+
executeDeepResource: deps.executeDeepResource,
|
|
312
|
+
});
|
|
313
|
+
let report = analyzedReport;
|
|
294
314
|
const remediationRequested = input.options.remediate ||
|
|
295
315
|
input.options.fixSafe ||
|
|
296
316
|
input.options.dryRun ||
|
|
@@ -378,7 +398,11 @@ export async function executeScanCommand(input, deps) {
|
|
|
378
398
|
const targetSummaryNote = input.config.output_format === "terminal"
|
|
379
399
|
? summarizeRequestedTargetFindings(report, input.displayTarget)
|
|
380
400
|
: null;
|
|
381
|
-
const scanNotes =
|
|
401
|
+
const scanNotes = input.config.output_format === "terminal"
|
|
402
|
+
? targetSummaryNote
|
|
403
|
+
? [...deepScanNotes, targetSummaryNote]
|
|
404
|
+
: deepScanNotes
|
|
405
|
+
: [];
|
|
382
406
|
if (shouldUseTui) {
|
|
383
407
|
deps.renderTui?.({ view: "dashboard", report, notices: scanNotes });
|
|
384
408
|
deps.renderTui?.({ view: "summary", report });
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type CodeGateConfig, type OutputFormat, type ResolveConfigOptions } from "../config.js";
|
|
2
2
|
import { type ResolvedScanTarget } from "../scan-target.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ScanAnalysisDeps, type ScanRunnerInput } from "./scan-command.js";
|
|
4
4
|
import type { CodeGateReport } from "../types/report.js";
|
|
5
5
|
export interface SkillsWrapperRuntimeOptions {
|
|
6
6
|
force: boolean;
|
|
7
|
+
deep: boolean;
|
|
7
8
|
noTui: boolean;
|
|
8
9
|
includeUserScope: boolean;
|
|
9
10
|
format?: OutputFormat;
|
|
@@ -34,6 +35,14 @@ export interface SkillsWrapperDeps {
|
|
|
34
35
|
pathExists?: (path: string) => boolean;
|
|
35
36
|
resolveConfig: (options: ResolveConfigOptions) => CodeGateConfig;
|
|
36
37
|
runScan: (input: ScanRunnerInput) => Promise<CodeGateReport>;
|
|
38
|
+
prepareScanDiscovery?: ScanAnalysisDeps["prepareScanDiscovery"];
|
|
39
|
+
discoverDeepResources?: ScanAnalysisDeps["discoverDeepResources"];
|
|
40
|
+
discoverLocalTextTargets?: ScanAnalysisDeps["discoverLocalTextTargets"];
|
|
41
|
+
requestDeepScanConsent?: ScanAnalysisDeps["requestDeepScanConsent"];
|
|
42
|
+
requestDeepAgentSelection?: ScanAnalysisDeps["requestDeepAgentSelection"];
|
|
43
|
+
requestMetaAgentCommandConsent?: ScanAnalysisDeps["requestMetaAgentCommandConsent"];
|
|
44
|
+
runMetaAgentCommand?: ScanAnalysisDeps["runMetaAgentCommand"];
|
|
45
|
+
executeDeepResource?: ScanAnalysisDeps["executeDeepResource"];
|
|
37
46
|
resolveScanTarget?: (input: {
|
|
38
47
|
rawTarget: string;
|
|
39
48
|
cwd: string;
|
|
@@ -2,10 +2,10 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { OUTPUT_FORMATS, } from "../config.js";
|
|
6
6
|
import { renderByFormat, summarizeRequestedTargetFindings } from "./scan-command/helpers.js";
|
|
7
|
-
import { reorderRequestedTargetFindings } from "../report/requested-target-findings.js";
|
|
8
7
|
import { resolveScanTarget } from "../scan-target.js";
|
|
8
|
+
import { runScanAnalysis } from "./scan-command.js";
|
|
9
9
|
function parseWrapperOptionValue(args, index, flag) {
|
|
10
10
|
const current = args[index] ?? "";
|
|
11
11
|
const withEquals = `${flag}=`;
|
|
@@ -177,6 +177,7 @@ function findAddSubcommandIndex(args, context) {
|
|
|
177
177
|
export function parseSkillsInvocation(rawArgs, context) {
|
|
178
178
|
const wrapper = {
|
|
179
179
|
force: false,
|
|
180
|
+
deep: false,
|
|
180
181
|
noTui: false,
|
|
181
182
|
includeUserScope: false,
|
|
182
183
|
format: undefined,
|
|
@@ -196,6 +197,10 @@ export function parseSkillsInvocation(rawArgs, context) {
|
|
|
196
197
|
wrapper.force = true;
|
|
197
198
|
continue;
|
|
198
199
|
}
|
|
200
|
+
if (token === "--cg-deep") {
|
|
201
|
+
wrapper.deep = true;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
199
204
|
if (token === "--cg-no-tui") {
|
|
200
205
|
wrapper.noTui = true;
|
|
201
206
|
continue;
|
|
@@ -320,42 +325,56 @@ export async function executeSkillsWrapper(input, deps) {
|
|
|
320
325
|
const config = parsed.wrapper.includeUserScope
|
|
321
326
|
? { ...baseConfig, scan_user_scope: true }
|
|
322
327
|
: baseConfig;
|
|
323
|
-
|
|
328
|
+
const { report, deepScanNotes } = await runScanAnalysis({
|
|
324
329
|
version: input.version,
|
|
325
330
|
scanTarget: resolvedTarget.scanTarget,
|
|
331
|
+
displayTarget: resolvedTarget.displayTarget,
|
|
332
|
+
explicitCandidates: resolvedTarget.explicitCandidates,
|
|
326
333
|
config,
|
|
327
|
-
|
|
334
|
+
options: {
|
|
328
335
|
noTui,
|
|
329
336
|
format: parsed.wrapper.format,
|
|
330
337
|
force: parsed.wrapper.force,
|
|
331
338
|
includeUserScope: parsed.wrapper.includeUserScope,
|
|
332
339
|
skill: preferredSkill,
|
|
340
|
+
deep: parsed.wrapper.deep,
|
|
333
341
|
},
|
|
334
|
-
|
|
342
|
+
}, {
|
|
343
|
+
isTTY: deps.isTTY,
|
|
344
|
+
runScan: deps.runScan,
|
|
345
|
+
prepareScanDiscovery: deps.prepareScanDiscovery,
|
|
346
|
+
discoverDeepResources: deps.discoverDeepResources,
|
|
347
|
+
discoverLocalTextTargets: deps.discoverLocalTextTargets,
|
|
348
|
+
requestDeepScanConsent: interactivePromptsEnabled ? deps.requestDeepScanConsent : undefined,
|
|
349
|
+
requestDeepAgentSelection: interactivePromptsEnabled
|
|
350
|
+
? deps.requestDeepAgentSelection
|
|
351
|
+
: undefined,
|
|
352
|
+
requestMetaAgentCommandConsent: interactivePromptsEnabled
|
|
353
|
+
? deps.requestMetaAgentCommandConsent
|
|
354
|
+
: undefined,
|
|
355
|
+
runMetaAgentCommand: deps.runMetaAgentCommand,
|
|
356
|
+
executeDeepResource: deps.executeDeepResource,
|
|
335
357
|
});
|
|
336
|
-
if (resolvedTarget.displayTarget && resolvedTarget.displayTarget !== report.scan_target) {
|
|
337
|
-
report = {
|
|
338
|
-
...report,
|
|
339
|
-
scan_target: resolvedTarget.displayTarget,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
report = applyConfigPolicy(report, config);
|
|
343
|
-
report = reorderRequestedTargetFindings(report, resolvedTarget.displayTarget);
|
|
344
358
|
const shouldUseTui = config.tui.enabled && isTTY && deps.renderTui !== undefined && noTui !== true;
|
|
345
359
|
const targetSummaryNote = config.output_format === "terminal"
|
|
346
360
|
? summarizeRequestedTargetFindings(report, resolvedTarget.displayTarget)
|
|
347
361
|
: null;
|
|
362
|
+
const scanNotes = config.output_format === "terminal"
|
|
363
|
+
? targetSummaryNote
|
|
364
|
+
? [...deepScanNotes, targetSummaryNote]
|
|
365
|
+
: deepScanNotes
|
|
366
|
+
: [];
|
|
348
367
|
if (shouldUseTui) {
|
|
349
368
|
deps.renderTui?.({
|
|
350
369
|
view: "dashboard",
|
|
351
370
|
report,
|
|
352
|
-
notices:
|
|
371
|
+
notices: scanNotes.length > 0 ? scanNotes : undefined,
|
|
353
372
|
});
|
|
354
373
|
deps.renderTui?.({ view: "summary", report });
|
|
355
374
|
}
|
|
356
375
|
else {
|
|
357
|
-
|
|
358
|
-
deps.stdout(
|
|
376
|
+
for (const note of scanNotes) {
|
|
377
|
+
deps.stdout(note);
|
|
359
378
|
}
|
|
360
379
|
deps.stdout(renderByFormat(config.output_format, report));
|
|
361
380
|
}
|