hackmyagent 0.8.1 → 0.9.1
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/cli.js +219 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/soul/index.d.ts +8 -0
- package/dist/soul/index.d.ts.map +1 -0
- package/dist/soul/index.js +14 -0
- package/dist/soul/index.js.map +1 -0
- package/dist/soul/scanner.d.ts +95 -0
- package/dist/soul/scanner.d.ts.map +1 -0
- package/dist/soul/scanner.js +411 -0
- package/dist/soul/scanner.js.map +1 -0
- package/dist/soul/templates.d.ts +12 -0
- package/dist/soul/templates.d.ts.map +1 -0
- package/dist/soul/templates.js +211 -0
- package/dist/soul/templates.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3530,5 +3530,224 @@ Examples:
|
|
|
3530
3530
|
process.exit(1);
|
|
3531
3531
|
}
|
|
3532
3532
|
});
|
|
3533
|
+
// Grade display colors
|
|
3534
|
+
function gradeColor(grade) {
|
|
3535
|
+
switch (grade) {
|
|
3536
|
+
case 'A': return colors.green;
|
|
3537
|
+
case 'B': return colors.green;
|
|
3538
|
+
case 'C': return colors.yellow;
|
|
3539
|
+
case 'D': return colors.red;
|
|
3540
|
+
case 'F': return colors.brightRed;
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
// Domain percentage bar for text output
|
|
3544
|
+
function domainBar(pct) {
|
|
3545
|
+
if (pct >= 80)
|
|
3546
|
+
return colors.green;
|
|
3547
|
+
if (pct >= 60)
|
|
3548
|
+
return colors.yellow;
|
|
3549
|
+
if (pct >= 40)
|
|
3550
|
+
return colors.yellow;
|
|
3551
|
+
return colors.red;
|
|
3552
|
+
}
|
|
3553
|
+
program
|
|
3554
|
+
.command('scan-soul')
|
|
3555
|
+
.description(`Scan behavioral governance coverage
|
|
3556
|
+
|
|
3557
|
+
Analyzes SOUL.md (or equivalent governance file) for coverage
|
|
3558
|
+
across 8 behavioral governance domains with 26 security controls.
|
|
3559
|
+
|
|
3560
|
+
Searches for governance files in priority order:
|
|
3561
|
+
SOUL.md > system-prompt.md > SYSTEM_PROMPT.md > .cursorrules
|
|
3562
|
+
> .github/copilot-instructions.md > CLAUDE.md > .clinerules
|
|
3563
|
+
> instructions.md > constitution.md > agent-config.yaml
|
|
3564
|
+
|
|
3565
|
+
Domains checked (OASB v2):
|
|
3566
|
+
7. Trust Hierarchy 8. Capability Boundaries
|
|
3567
|
+
9. Injection Hardening 10. Data Handling
|
|
3568
|
+
11. Hardcoded Behaviors 12. Agentic Safety
|
|
3569
|
+
13. Honesty & Transparency 14. Human Oversight
|
|
3570
|
+
|
|
3571
|
+
Grade: A (80-100), B (60-79), C (40-59), D (20-39), F (0-19)
|
|
3572
|
+
Critical floor: Missing SOUL-IH-003 or SOUL-HB-001 caps grade at C.
|
|
3573
|
+
|
|
3574
|
+
Examples:
|
|
3575
|
+
$ hackmyagent scan-soul Scan current directory
|
|
3576
|
+
$ hackmyagent scan-soul ./my-agent Scan specific directory
|
|
3577
|
+
$ hackmyagent scan-soul --json Machine-readable output
|
|
3578
|
+
$ hackmyagent scan-soul --verbose Show all controls`)
|
|
3579
|
+
.argument('[directory]', 'Directory to scan (defaults to current directory)', '.')
|
|
3580
|
+
.option('--json', 'Output as JSON')
|
|
3581
|
+
.option('-v, --verbose', 'Show individual control results')
|
|
3582
|
+
.option('--tier <tier>', 'Override agent tier detection (BASIC, TOOL-USING, AGENTIC, MULTI-AGENT)')
|
|
3583
|
+
.option('--fail-below <score>', 'Exit 1 if score below threshold (0-100)')
|
|
3584
|
+
.action(async (directory, options) => {
|
|
3585
|
+
try {
|
|
3586
|
+
const targetDir = directory.startsWith('/') ? directory : process.cwd() + '/' + directory;
|
|
3587
|
+
if (!require('fs').existsSync(targetDir)) {
|
|
3588
|
+
process.stderr.write(`Error: Directory '${targetDir}' does not exist.\n`);
|
|
3589
|
+
process.exit(1);
|
|
3590
|
+
}
|
|
3591
|
+
const scanner = new index_1.SoulScanner();
|
|
3592
|
+
const result = await scanner.scanSoul(targetDir, {
|
|
3593
|
+
verbose: options.verbose,
|
|
3594
|
+
tier: options.tier,
|
|
3595
|
+
});
|
|
3596
|
+
// JSON output
|
|
3597
|
+
if (options.json) {
|
|
3598
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
3599
|
+
// Check fail threshold
|
|
3600
|
+
if (options.failBelow) {
|
|
3601
|
+
const threshold = parseInt(options.failBelow, 10);
|
|
3602
|
+
if (!isNaN(threshold) && result.score < threshold) {
|
|
3603
|
+
process.exit(1);
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
return;
|
|
3607
|
+
}
|
|
3608
|
+
// Text output
|
|
3609
|
+
process.stdout.write('\nOASB v2 Behavioral Governance Scan\n');
|
|
3610
|
+
process.stdout.write('----------------------------------------------------\n\n');
|
|
3611
|
+
if (result.file) {
|
|
3612
|
+
process.stdout.write(`File: ${result.file} (${result.fileSize.toLocaleString()} chars)\n`);
|
|
3613
|
+
}
|
|
3614
|
+
else {
|
|
3615
|
+
process.stdout.write(`File: ${colors.red}No governance file found${colors.reset}\n`);
|
|
3616
|
+
process.stdout.write(` Searched: ${['SOUL.md', 'system-prompt.md', 'CLAUDE.md', '...'].join(', ')}\n`);
|
|
3617
|
+
}
|
|
3618
|
+
process.stdout.write(`Agent Tier: ${result.agentTier} (auto-detected)\n\n`);
|
|
3619
|
+
process.stdout.write('Domain Scores:\n');
|
|
3620
|
+
for (const domain of result.domains) {
|
|
3621
|
+
const pctColor = domainBar(domain.percentage);
|
|
3622
|
+
const label = (domain.domain + ':').padEnd(26);
|
|
3623
|
+
process.stdout.write(` ${label}${pctColor}${domain.passed}/${domain.total} (${domain.percentage}%)${colors.reset}\n`);
|
|
3624
|
+
// Verbose: show individual controls
|
|
3625
|
+
if (options.verbose) {
|
|
3626
|
+
for (const ctrl of domain.controls) {
|
|
3627
|
+
const status = ctrl.passed
|
|
3628
|
+
? `${colors.green}PASS${colors.reset}`
|
|
3629
|
+
: `${colors.red}FAIL${colors.reset}`;
|
|
3630
|
+
process.stdout.write(` ${ctrl.id}: ${status} ${ctrl.name}\n`);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
process.stdout.write('\n');
|
|
3635
|
+
// Score and grade
|
|
3636
|
+
const gc = gradeColor(result.grade);
|
|
3637
|
+
process.stdout.write(`Governance Score: ${gc}${result.score}/100 (Grade: ${result.grade})${colors.reset}\n`);
|
|
3638
|
+
if (result.criticalFloor) {
|
|
3639
|
+
process.stdout.write(`${colors.yellow}Critical Floor: APPLIED${colors.reset} (${result.criticalMissing.join(', ')} missing)\n`);
|
|
3640
|
+
}
|
|
3641
|
+
// Path forward
|
|
3642
|
+
const missing = result.totalControls - result.totalPassed;
|
|
3643
|
+
if (missing > 0) {
|
|
3644
|
+
process.stdout.write(`\n${missing} control${missing === 1 ? '' : 's'} missing.`);
|
|
3645
|
+
process.stdout.write(` Run '${colors.cyan}hackmyagent harden-soul${colors.reset}' to remediate.\n`);
|
|
3646
|
+
}
|
|
3647
|
+
else {
|
|
3648
|
+
process.stdout.write(`\n${colors.green}All ${result.totalControls} governance controls covered.${colors.reset}\n`);
|
|
3649
|
+
}
|
|
3650
|
+
process.stdout.write('\n');
|
|
3651
|
+
// Check fail threshold
|
|
3652
|
+
if (options.failBelow) {
|
|
3653
|
+
const threshold = parseInt(options.failBelow, 10);
|
|
3654
|
+
if (!isNaN(threshold) && result.score < threshold) {
|
|
3655
|
+
process.stderr.write(`Score ${result.score} is below threshold ${threshold}\n`);
|
|
3656
|
+
process.exit(1);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
catch (error) {
|
|
3661
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`);
|
|
3662
|
+
process.exit(1);
|
|
3663
|
+
}
|
|
3664
|
+
});
|
|
3665
|
+
program
|
|
3666
|
+
.command('harden-soul')
|
|
3667
|
+
.description(`Generate or update SOUL.md with missing governance sections
|
|
3668
|
+
|
|
3669
|
+
Runs scan-soul internally to identify missing controls, then generates
|
|
3670
|
+
template content for each missing domain. Existing content is preserved.
|
|
3671
|
+
|
|
3672
|
+
Modes:
|
|
3673
|
+
Default: Append missing sections to SOUL.md (or create it)
|
|
3674
|
+
--dry-run: Preview what would be added without modifying files
|
|
3675
|
+
|
|
3676
|
+
Examples:
|
|
3677
|
+
$ hackmyagent harden-soul Add missing sections
|
|
3678
|
+
$ hackmyagent harden-soul --dry-run Preview changes
|
|
3679
|
+
$ hackmyagent harden-soul ./my-agent Target specific directory
|
|
3680
|
+
$ hackmyagent harden-soul --json Machine-readable output`)
|
|
3681
|
+
.argument('[directory]', 'Directory to harden (defaults to current directory)', '.')
|
|
3682
|
+
.option('--dry-run', 'Preview changes without modifying files')
|
|
3683
|
+
.option('--json', 'Output as JSON')
|
|
3684
|
+
.action(async (directory, options) => {
|
|
3685
|
+
try {
|
|
3686
|
+
const targetDir = directory.startsWith('/') ? directory : process.cwd() + '/' + directory;
|
|
3687
|
+
if (!require('fs').existsSync(targetDir)) {
|
|
3688
|
+
process.stderr.write(`Error: Directory '${targetDir}' does not exist.\n`);
|
|
3689
|
+
process.exit(1);
|
|
3690
|
+
}
|
|
3691
|
+
const scanner = new index_1.SoulScanner();
|
|
3692
|
+
const result = await scanner.hardenSoul(targetDir, { dryRun: options.dryRun });
|
|
3693
|
+
// JSON output
|
|
3694
|
+
if (options.json) {
|
|
3695
|
+
// Exclude full content from JSON to keep it concise
|
|
3696
|
+
const jsonResult = {
|
|
3697
|
+
file: result.file,
|
|
3698
|
+
sectionsAdded: result.sectionsAdded,
|
|
3699
|
+
controlsAdded: result.controlsAdded,
|
|
3700
|
+
dryRun: result.dryRun,
|
|
3701
|
+
existedBefore: result.existedBefore,
|
|
3702
|
+
};
|
|
3703
|
+
process.stdout.write(JSON.stringify(jsonResult, null, 2) + '\n');
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
// Text output
|
|
3707
|
+
if (result.sectionsAdded.length === 0) {
|
|
3708
|
+
process.stdout.write(`\n${colors.green}All governance domains already have sections in ${result.file}.${colors.reset}\n`);
|
|
3709
|
+
process.stdout.write(`Run 'hackmyagent scan-soul --verbose' to see individual control coverage.\n\n`);
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
if (result.dryRun) {
|
|
3713
|
+
process.stdout.write('\nHarden SOUL (dry-run)\n');
|
|
3714
|
+
process.stdout.write('----------------------------------------------------\n\n');
|
|
3715
|
+
process.stdout.write(`Target: ${result.file}`);
|
|
3716
|
+
if (result.existedBefore) {
|
|
3717
|
+
process.stdout.write(' (append)\n');
|
|
3718
|
+
}
|
|
3719
|
+
else {
|
|
3720
|
+
process.stdout.write(' (create)\n');
|
|
3721
|
+
}
|
|
3722
|
+
process.stdout.write(`Sections to add: ${result.sectionsAdded.length}\n`);
|
|
3723
|
+
process.stdout.write(`Controls covered: +${result.controlsAdded}\n\n`);
|
|
3724
|
+
process.stdout.write('Sections:\n');
|
|
3725
|
+
for (const section of result.sectionsAdded) {
|
|
3726
|
+
process.stdout.write(` ${colors.cyan}+${colors.reset} ${section}\n`);
|
|
3727
|
+
}
|
|
3728
|
+
process.stdout.write(`\nRun without --dry-run to apply changes.\n\n`);
|
|
3729
|
+
}
|
|
3730
|
+
else {
|
|
3731
|
+
process.stdout.write('\nHarden SOUL\n');
|
|
3732
|
+
process.stdout.write('----------------------------------------------------\n\n');
|
|
3733
|
+
if (result.existedBefore) {
|
|
3734
|
+
process.stdout.write(`Updated: ${result.file}\n`);
|
|
3735
|
+
}
|
|
3736
|
+
else {
|
|
3737
|
+
process.stdout.write(`Created: ${result.file}\n`);
|
|
3738
|
+
}
|
|
3739
|
+
process.stdout.write(`Added ${result.sectionsAdded.length} section${result.sectionsAdded.length === 1 ? '' : 's'}:\n`);
|
|
3740
|
+
for (const section of result.sectionsAdded) {
|
|
3741
|
+
process.stdout.write(` ${colors.green}+${colors.reset} ${section}\n`);
|
|
3742
|
+
}
|
|
3743
|
+
process.stdout.write(`Controls covered: +${result.controlsAdded}\n\n`);
|
|
3744
|
+
process.stdout.write(`Run '${colors.cyan}hackmyagent scan-soul${colors.reset}' to verify coverage.\n\n`);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
catch (error) {
|
|
3748
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`);
|
|
3749
|
+
process.exit(1);
|
|
3750
|
+
}
|
|
3751
|
+
});
|
|
3533
3752
|
program.parse();
|
|
3534
3753
|
//# sourceMappingURL=cli.js.map
|