fullcourtdefense-cli 1.28.0 → 1.30.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/dist/actionPolicyEngine.d.ts +22 -0
- package/dist/actionPolicyEngine.js +446 -22
- package/dist/blockExplanation.d.ts +95 -0
- package/dist/blockExplanation.js +240 -0
- package/dist/commands/cmdGuard.d.ts +9 -0
- package/dist/commands/cmdGuard.js +27 -3
- package/dist/commands/deterministicGuard.d.ts +35 -0
- package/dist/commands/deterministicGuard.js +49 -35
- package/dist/commands/hook.js +116 -21
- package/dist/commands/mcpGateway.js +53 -14
- package/dist/commands/posixShellGuard.d.ts +12 -0
- package/dist/commands/posixShellGuard.js +8 -0
- package/dist/commands/shellGuard.d.ts +12 -0
- package/dist/commands/shellGuard.js +102 -13
- package/dist/notify.d.ts +2 -0
- package/dist/notify.js +5 -0
- package/dist/version.json +1 -1
- package/package.json +6 -2
|
@@ -39,10 +39,14 @@ exports.guardArtifactStale = guardArtifactStale;
|
|
|
39
39
|
exports.writeGuardArtifact = writeGuardArtifact;
|
|
40
40
|
exports.guardCliEntry = guardCliEntry;
|
|
41
41
|
exports.toShellGuardAction = toShellGuardAction;
|
|
42
|
+
exports.builtinRuleIds = builtinRuleIds;
|
|
43
|
+
exports.builtinRulesAllOn = builtinRulesAllOn;
|
|
44
|
+
exports.builtinRuleCommandNames = builtinRuleCommandNames;
|
|
42
45
|
exports.writeShellGuardRules = writeShellGuardRules;
|
|
43
46
|
exports.activeShellGuardRules = activeShellGuardRules;
|
|
44
47
|
exports.evaluateShellCommand = evaluateShellCommand;
|
|
45
48
|
exports.refreshShellGuardRules = refreshShellGuardRules;
|
|
49
|
+
exports.buildGuardPs1 = buildGuardPs1;
|
|
46
50
|
exports.resolveDocumentsFolder = resolveDocumentsFolder;
|
|
47
51
|
exports.getShellGuardStatus = getShellGuardStatus;
|
|
48
52
|
exports.isShellGuardInstalled = isShellGuardInstalled;
|
|
@@ -52,6 +56,8 @@ const child_process_1 = require("child_process");
|
|
|
52
56
|
const fs = __importStar(require("fs"));
|
|
53
57
|
const os = __importStar(require("os"));
|
|
54
58
|
const path = __importStar(require("path"));
|
|
59
|
+
const deterministicGuard_1 = require("./deterministicGuard");
|
|
60
|
+
const notify_1 = require("../notify");
|
|
55
61
|
/**
|
|
56
62
|
* Interactive PowerShell guard — real-time blocking of dangerous commands
|
|
57
63
|
* TYPED by a human in a terminal (inside or outside the IDE).
|
|
@@ -195,7 +201,10 @@ const BUILTIN_RULES = [
|
|
|
195
201
|
command: ['rd', 'rmdir'], flags: [String.raw `/s\b`], target: [String.raw `["']?[a-z]:[\\/]?["']?\s*(?:$|[;&|])`] }),
|
|
196
202
|
compileSpec({ id: 'windows_system_dir_delete', category: 'destructive_command', severity: 'critical', reason: 'recursive delete of a Windows system directory',
|
|
197
203
|
command: ['del', 'erase', 'rd', 'rmdir'], flags: [String.raw `/s\b`], target: [T_WIN_SYSDIR] }),
|
|
198
|
-
|
|
204
|
+
// Same catalog item as the `del /s` form above (windows_drive_delete): ONE
|
|
205
|
+
// console toggle, two syntaxes. Duplicate ids are intended — they share a
|
|
206
|
+
// switch and an action.
|
|
207
|
+
compileSpec({ id: 'windows_drive_delete', category: 'destructive_command', severity: 'critical', reason: 'recursive Remove-Item on a drive root',
|
|
199
208
|
command: ['remove-item', 'ri'], flags: [String.raw `-recurse\w*`], target: [String.raw `["']?[a-z]:[\\/]?["']?(?:\s|$|[;&|])`] }),
|
|
200
209
|
compileSpec({ id: 'chmod_root_777', category: 'destructive_command', severity: 'high', reason: 'recursive world-writable permission change on root',
|
|
201
210
|
command: ['chmod'], flags: [String.raw `-[a-z]*r[a-z]*|--recursive`, String.raw `[0-7]?777`], target: [T_UNIX_ROOT] }),
|
|
@@ -243,8 +252,16 @@ const BUILTIN_RULES = [
|
|
|
243
252
|
{ id: 'php_reverse_shell', category: 'reverse_shell', severity: 'high', reason: 'php reverse shell', source: 'builtin',
|
|
244
253
|
pattern: String.raw `\bphp\s+-r\b(?=.*fsockopen)` },
|
|
245
254
|
// Cloud metadata / credential exfiltration
|
|
246
|
-
|
|
247
|
-
|
|
255
|
+
// One terminal rule per CONSOLE toggle (catalog ids) — a single combined rule
|
|
256
|
+
// meant switching off / asking on one endpoint changed nothing in a shell.
|
|
257
|
+
{ id: 'aws_gcp_metadata_ip', category: 'metadata_ssrf', severity: 'high', reason: 'request to the AWS/GCP metadata endpoint (169.254.169.254)', source: 'builtin',
|
|
258
|
+
pattern: String.raw `(?=.*\b(?:curl|wget|iwr|invoke-webrequest|invoke-restmethod|irm)\b)(?=.*169\.254\.169\.254)` },
|
|
259
|
+
{ id: 'gcp_metadata_host', category: 'metadata_ssrf', severity: 'high', reason: 'request to the GCP metadata host (metadata.google.internal)', source: 'builtin',
|
|
260
|
+
pattern: String.raw `(?=.*\b(?:curl|wget|iwr|invoke-webrequest|invoke-restmethod|irm)\b)(?=.*metadata\.google\.internal)` },
|
|
261
|
+
{ id: 'azure_metadata_host', category: 'metadata_ssrf', severity: 'high', reason: 'request to the Azure metadata host (metadata.azure.com)', source: 'builtin',
|
|
262
|
+
pattern: String.raw `(?=.*\b(?:curl|wget|iwr|invoke-webrequest|invoke-restmethod|irm)\b)(?=.*metadata\.azure\.com)` },
|
|
263
|
+
{ id: 'alibaba_metadata_ip', category: 'metadata_ssrf', severity: 'high', reason: 'request to the Alibaba metadata endpoint (100.100.100.200)', source: 'builtin',
|
|
264
|
+
pattern: String.raw `(?=.*\b(?:curl|wget|iwr|invoke-webrequest|invoke-restmethod|irm)\b)(?=.*100\.100\.100\.200)` },
|
|
248
265
|
{ id: 'credential_exfiltration', category: 'secret_exfiltration', severity: 'critical', reason: 'credential file sent to the network', source: 'builtin',
|
|
249
266
|
pattern: String.raw `(?=.*\b(?:curl|wget|iwr|invoke-webrequest|invoke-restmethod|scp|nc|ncat)\b)(?=.*(?:\.ssh[\\/]id_(?:rsa|ed25519|ecdsa|dsa)|\.aws[\\/]credentials|application_default_credentials\.json|\.npmrc|\.netrc|\.kube[\\/]config))` },
|
|
250
267
|
// Infra destroy — spec-compiled where flags matter (order-independent).
|
|
@@ -268,6 +285,10 @@ const BUILTIN_RULES = [
|
|
|
268
285
|
command: ['kubectl'], extra: [String.raw `\bdelete\s+(?:pvc|persistentvolumeclaims?)\b`], flags: [String.raw `--all`] }),
|
|
269
286
|
{ id: 'kubectl_delete_namespace', category: 'infra_destroy', severity: 'high', reason: 'destructive kubectl namespace delete', source: 'builtin',
|
|
270
287
|
pattern: String.raw `\bkubectl\s+delete\s+(?:namespace|ns)\b` },
|
|
288
|
+
// Catalog item (strict, OFF by default — DEFAULT_DISABLED_BUILT_INS): any
|
|
289
|
+
// kubectl delete. Present so the console toggle means something in a shell.
|
|
290
|
+
{ id: 'generic_kubectl_delete', category: 'infra_destroy', severity: 'medium', reason: 'kubectl delete (any resource)', source: 'builtin',
|
|
291
|
+
pattern: String.raw `\bkubectl\s+delete\b` },
|
|
271
292
|
{ id: 'gcloud_project_delete', category: 'infra_destroy', severity: 'high', reason: 'GCP project deletion', source: 'builtin',
|
|
272
293
|
pattern: String.raw `\bgcloud\s+projects\s+delete\b` },
|
|
273
294
|
{ id: 'gcloud_sql_delete', category: 'infra_destroy', severity: 'high', reason: 'GCP SQL instance deletion', source: 'builtin',
|
|
@@ -287,10 +308,11 @@ const BUILTIN_RULES = [
|
|
|
287
308
|
// Version-control footguns — imported from shellfirm / Sigma (git group).
|
|
288
309
|
compileSpec({ id: 'git_force_push', category: 'vcs_danger', severity: 'high', reason: 'git force push (overwrites remote history)',
|
|
289
310
|
command: ['git'], extra: [String.raw `\bpush\b`], flags: [String.raw `--force|-f\b`], neg: [String.raw `\s--force-with-lease`] }),
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
311
|
+
// One catalog item covers both forms (console: "Git destructive clean/reset",
|
|
312
|
+
// OFF by default) — same id + regex as the IDE guard, so the console toggle
|
|
313
|
+
// means the same thing in a terminal as it does for an agent.
|
|
314
|
+
{ id: 'git_clean_force', category: 'vcs_danger', severity: 'medium', reason: 'destructive git clean/reset (discards local work)', source: 'builtin',
|
|
315
|
+
pattern: String.raw `\bgit\s+(?:reset\s+--hard|clean\s+-f(?:d|x|dx)?\b)` },
|
|
294
316
|
{ id: 'git_branch_delete_force', category: 'vcs_danger', severity: 'low', reason: 'git force-delete branch', source: 'builtin',
|
|
295
317
|
pattern: String.raw `\bgit\s+branch\b(?=[^;&|]*\s-D\b)` },
|
|
296
318
|
// Anti-forensics / tamper
|
|
@@ -321,7 +343,38 @@ const BUILTIN_RULES = [
|
|
|
321
343
|
pattern: String.raw `\btruncate\s+table\b` },
|
|
322
344
|
{ id: 'drop_table', category: 'destructive_sql', severity: 'medium', reason: 'DROP TABLE', source: 'builtin',
|
|
323
345
|
pattern: String.raw `\bdrop\s+table\b` },
|
|
346
|
+
// Credential-revealing commands — the SAME table the IDE/agent guard
|
|
347
|
+
// evaluates (deterministicGuard.CREDENTIAL_COMMAND_RULES). One definition,
|
|
348
|
+
// every surface: a rule the console shows as "asks the developer" must ask
|
|
349
|
+
// the human typing `gh auth token` in PowerShell exactly as it asks an agent.
|
|
350
|
+
...deterministicGuard_1.CREDENTIAL_COMMAND_RULES.map((rule) => ({
|
|
351
|
+
id: rule.itemId,
|
|
352
|
+
category: rule.categoryId,
|
|
353
|
+
severity: rule.severity,
|
|
354
|
+
reason: rule.reason,
|
|
355
|
+
source: 'builtin',
|
|
356
|
+
pattern: rule.pattern,
|
|
357
|
+
})),
|
|
324
358
|
];
|
|
359
|
+
/**
|
|
360
|
+
* Leading command names that anchor a built-in rule — what a first-token hook
|
|
361
|
+
* (cmd.exe doskey) can key on. Derived from the shared tables so a new rule
|
|
362
|
+
* cannot be added without its command name reaching the cmd guard's index.
|
|
363
|
+
*/
|
|
364
|
+
/** Every built-in terminal rule id, ignoring console toggles (parity tests, docs). */
|
|
365
|
+
function builtinRuleIds() {
|
|
366
|
+
return BUILTIN_RULES.map(rule => rule.id);
|
|
367
|
+
}
|
|
368
|
+
/** Every built-in terminal rule, all switched ON with default action (parity tests). */
|
|
369
|
+
function builtinRulesAllOn() {
|
|
370
|
+
return BUILTIN_RULES.map(rule => ({ ...rule, action: 'block' }));
|
|
371
|
+
}
|
|
372
|
+
function builtinRuleCommandNames() {
|
|
373
|
+
const names = new Set();
|
|
374
|
+
for (const rule of deterministicGuard_1.CREDENTIAL_COMMAND_RULES)
|
|
375
|
+
rule.commands.forEach(name => names.add(name));
|
|
376
|
+
return Array.from(names).sort();
|
|
377
|
+
}
|
|
325
378
|
function escapeDotNetRegex(literal) {
|
|
326
379
|
return literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
327
380
|
}
|
|
@@ -348,6 +401,7 @@ function cachedMode() {
|
|
|
348
401
|
*/
|
|
349
402
|
function cachedDisabledItemIds() {
|
|
350
403
|
const disabled = new Set();
|
|
404
|
+
let snapshots = 0;
|
|
351
405
|
try {
|
|
352
406
|
for (const file of fs.readdirSync(SNAPSHOT_CACHE_DIR)) {
|
|
353
407
|
if (!file.startsWith('local-safety-') || !file.endsWith('.json'))
|
|
@@ -356,6 +410,7 @@ function cachedDisabledItemIds() {
|
|
|
356
410
|
const parsed = JSON.parse(fs.readFileSync(path.join(SNAPSHOT_CACHE_DIR, file), 'utf8'));
|
|
357
411
|
const ids = parsed?.snapshot?.disabledBuiltInItemIds;
|
|
358
412
|
if (Array.isArray(ids)) {
|
|
413
|
+
snapshots++;
|
|
359
414
|
for (const id of ids) {
|
|
360
415
|
if (typeof id === 'string' && id)
|
|
361
416
|
disabled.add(id);
|
|
@@ -366,6 +421,14 @@ function cachedDisabledItemIds() {
|
|
|
366
421
|
}
|
|
367
422
|
}
|
|
368
423
|
catch { /* no snapshot cache yet */ }
|
|
424
|
+
// No console snapshot on this machine yet (fresh install, wiped cache): fall
|
|
425
|
+
// back to the catalog's own default-off set — the same set the IDE/agent
|
|
426
|
+
// guard applies offline — so a shell never enforces MORE than the IDE does.
|
|
427
|
+
// The moment a snapshot exists it is authoritative (it lists every off item).
|
|
428
|
+
if (snapshots === 0) {
|
|
429
|
+
for (const id of deterministicGuard_1.DEFAULT_DISABLED_BUILT_INS)
|
|
430
|
+
disabled.add(id);
|
|
431
|
+
}
|
|
369
432
|
return disabled;
|
|
370
433
|
}
|
|
371
434
|
/**
|
|
@@ -563,12 +626,31 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
563
626
|
`if ($null -eq $global:FcdAskRemembered) { $global:FcdAskRemembered = @{} }`,
|
|
564
627
|
`$global:FcdAskTimeoutSeconds = ${exports.SHELL_ASK_TIMEOUT_SECONDS}`,
|
|
565
628
|
`$global:FcdAskRememberHours = ${exports.SHELL_ASK_REMEMBER_HOURS}`,
|
|
629
|
+
`# Console page that controls shell rules — printed on every block/ask so the`,
|
|
630
|
+
`# developer knows exactly which rule (by id) stopped them and where it is changed.`,
|
|
631
|
+
`$global:FcdRulesUrl = ${psQuote((0, notify_1.shellRulesConsoleUrl)())}`,
|
|
632
|
+
`function global:Write-FcdUnblockHint {`,
|
|
633
|
+
` param($Rule)`,
|
|
634
|
+
` try {`,
|
|
635
|
+
` $origin = if ([string]$Rule.Source -eq 'custom') { 'org custom rule' } else { 'built-in Local Safety rule' }`,
|
|
636
|
+
` Write-Host ('Rule: "' + $Rule.Reason + '" (' + $origin + ', id ' + $Rule.Id + '). To remove this block an admin can set it to Ask / Warn only / Off here: ' + $global:FcdRulesUrl + '&rule=' + $Rule.Id) -ForegroundColor DarkGray`,
|
|
637
|
+
` } catch { }`,
|
|
638
|
+
`}`,
|
|
566
639
|
``,
|
|
567
|
-
|
|
568
|
-
|
|
640
|
+
`# Rules + mode are (re)loaded whenever the rules file changes on disk, so a`,
|
|
641
|
+
`# console change (rule -> Block, machine -> Protected) reaches THIS open`,
|
|
642
|
+
`# window as soon as the daemon syncs it — same as cmd.exe and bash/zsh,`,
|
|
643
|
+
`# which re-read on every command. Cost: one file-stat per command.`,
|
|
644
|
+
`$global:FcdRulesStamp = ''`,
|
|
645
|
+
`function global:Sync-FcdGuardRules {`,
|
|
646
|
+
` try {`,
|
|
647
|
+
` if (-not (Test-Path $global:FcdRulesPath)) { return }`,
|
|
648
|
+
` $fcdItem = Get-Item -LiteralPath $global:FcdRulesPath`,
|
|
649
|
+
` $stamp = [string]$fcdItem.LastWriteTimeUtc.Ticks + ':' + [string]$fcdItem.Length`,
|
|
650
|
+
` if ($stamp -eq $global:FcdRulesStamp) { return }`,
|
|
569
651
|
` $fcdRaw = Get-Content -Raw -Path $global:FcdRulesPath | ConvertFrom-Json`,
|
|
570
|
-
` if ($fcdRaw.mode -eq 'block') {
|
|
571
|
-
` $
|
|
652
|
+
` $mode = if ($fcdRaw.mode -eq 'block') { 'block' } else { 'monitor' }`,
|
|
653
|
+
` $rules = @($fcdRaw.rules | ForEach-Object {`,
|
|
572
654
|
` try {`,
|
|
573
655
|
` [pscustomobject]@{`,
|
|
574
656
|
` Id = [string]$_.id`,
|
|
@@ -581,8 +663,12 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
581
663
|
` }`,
|
|
582
664
|
` } catch { $null }`,
|
|
583
665
|
` } | Where-Object { $null -ne $_ })`,
|
|
584
|
-
`
|
|
585
|
-
`
|
|
666
|
+
` $global:FcdGuardMode = $mode`,
|
|
667
|
+
` $global:FcdGuardRules = $rules`,
|
|
668
|
+
` $global:FcdRulesStamp = $stamp`,
|
|
669
|
+
` } catch { <# keep the last good rules; a half-written file must not disarm the guard #> }`,
|
|
670
|
+
`}`,
|
|
671
|
+
`Sync-FcdGuardRules`,
|
|
586
672
|
``,
|
|
587
673
|
`# Execution view (aligned with the CLI's stripInertDataSegments): quoted`,
|
|
588
674
|
`# spans containing whitespace are prose — inert BY DEFAULT — unless in a`,
|
|
@@ -612,6 +698,7 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
612
698
|
`function global:Test-FcdShellGuard {`,
|
|
613
699
|
` param([string]$CommandLine)`,
|
|
614
700
|
` if ([string]::IsNullOrWhiteSpace($CommandLine)) { return $null }`,
|
|
701
|
+
` try { Sync-FcdGuardRules } catch { }`,
|
|
615
702
|
` $normalized = ($CommandLine -replace '\\s+', ' ').Trim()`,
|
|
616
703
|
` $execView = $CommandLine`,
|
|
617
704
|
` try { $execView = Get-FcdExecView -CommandLine $CommandLine } catch { }`,
|
|
@@ -756,6 +843,7 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
756
843
|
` }`,
|
|
757
844
|
` Write-Host ''`,
|
|
758
845
|
` Write-Host ('[FullCourtDefense] ASK [' + $hit.Severity + ']: ' + $hit.Reason + ' (rule ' + $hit.Id + ')') -ForegroundColor Yellow`,
|
|
846
|
+
` Write-FcdUnblockHint -Rule $hit`,
|
|
759
847
|
` Write-Host ('Run anyway? [y] yes [s] yes, remember for this shell [N] no (' + $global:FcdAskTimeoutSeconds + 's): ') -NoNewline`,
|
|
760
848
|
` $answer = Read-FcdAskAnswer -TimeoutSeconds $global:FcdAskTimeoutSeconds`,
|
|
761
849
|
` if ($answer -eq 'y' -or $answer -eq 's') {`,
|
|
@@ -792,6 +880,7 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
792
880
|
` Write-Host ''`,
|
|
793
881
|
` Write-Host ('[FullCourtDefense] BLOCKED [' + $hit.Severity + ']: ' + $hit.Reason + ' (rule ' + $hit.Id + ')') -ForegroundColor Red`,
|
|
794
882
|
` Write-Host 'This command was not executed. Reported to your security dashboard.' -ForegroundColor DarkGray`,
|
|
883
|
+
` Write-FcdUnblockHint -Rule $hit`,
|
|
795
884
|
` try { [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() } catch { }`,
|
|
796
885
|
` Write-FcdGuardEvent -Rule $hit -CommandLine $line -Decision 'block'`,
|
|
797
886
|
` [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()`,
|
package/dist/notify.d.ts
CHANGED
|
@@ -28,3 +28,5 @@ export declare function notifyOs(n: OsNotification): void;
|
|
|
28
28
|
export declare function consoleUrl(pathname?: string): string;
|
|
29
29
|
/** Deep link to the approvals queue in the console. */
|
|
30
30
|
export declare function approvalsConsoleUrl(): string;
|
|
31
|
+
/** Console page that controls shell-command (Local Safety) rules; append `&rule=<id>` to focus one. */
|
|
32
|
+
export declare function shellRulesConsoleUrl(): string;
|
package/dist/notify.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.notifyOs = notifyOs;
|
|
4
4
|
exports.consoleUrl = consoleUrl;
|
|
5
5
|
exports.approvalsConsoleUrl = approvalsConsoleUrl;
|
|
6
|
+
exports.shellRulesConsoleUrl = shellRulesConsoleUrl;
|
|
6
7
|
const child_process_1 = require("child_process");
|
|
7
8
|
function spawnDetached(command, args, opts) {
|
|
8
9
|
try {
|
|
@@ -137,3 +138,7 @@ function consoleUrl(pathname = '') {
|
|
|
137
138
|
function approvalsConsoleUrl() {
|
|
138
139
|
return consoleUrl('/agent-security/reports?tab=approvals');
|
|
139
140
|
}
|
|
141
|
+
/** Console page that controls shell-command (Local Safety) rules; append `&rule=<id>` to focus one. */
|
|
142
|
+
function shellRulesConsoleUrl() {
|
|
143
|
+
return consoleUrl('/agent-security/users?view=policies&rules=shell');
|
|
144
|
+
}
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"test:cmd-guard": "npm run build && node scripts/test-cmd-guard.js",
|
|
33
33
|
"test:cmd-guard-perf": "npm run build && node scripts/test-cmd-guard-perf.js",
|
|
34
34
|
"test:posix-guard": "npm run build && node scripts/test-posix-guard.js",
|
|
35
|
+
"test:shell-parity": "npm run build && node scripts/test-shell-parity.js",
|
|
35
36
|
"test:secret-posture": "npm run build && node scripts/test-secret-posture-fixtures.js",
|
|
36
37
|
"test:agent-file-posture": "npm run build && node scripts/test-agent-file-posture.js",
|
|
37
38
|
"test:realworld-posture": "npm run build && node scripts/test-realworld-posture-fixtures.js",
|
|
@@ -85,8 +86,11 @@
|
|
|
85
86
|
"test:clipboard-scan": "npm run build && node scripts/test-clipboard-scan.js",
|
|
86
87
|
"test:no-script-host": "npm run build && node scripts/test-no-script-host.js",
|
|
87
88
|
"test:s4u-migration": "npm run build && node scripts/test-s4u-task-migration.js",
|
|
89
|
+
"test:block-explanation": "npm run build && node scripts/test-block-explanation.js",
|
|
90
|
+
"test:ide-fp-corpus": "npm run build && node scripts/test-ide-fp-corpus.js",
|
|
91
|
+
"test:ide-fp-corpus:hook": "npm run build && node scripts/test-ide-fp-corpus.js --hook",
|
|
88
92
|
"build:msi": "powershell -NoProfile -ExecutionPolicy Bypass -File installer/windows/Build-Msi.ps1",
|
|
89
|
-
"prepublishOnly": "npm run build"
|
|
93
|
+
"prepublishOnly": "npm run build && node scripts/test-shell-parity.js && node scripts/test-terminal-mcp-ask.js"
|
|
90
94
|
},
|
|
91
95
|
"keywords": [
|
|
92
96
|
"llm",
|