@red-codes/agentguard 1.0.0 → 1.1.2
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 +179 -298
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +29185 -0
- package/dist/bin.js.map +7 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js.map +1 -0
- package/dist/commands/adoption.d.ts +2 -0
- package/dist/commands/adoption.d.ts.map +1 -0
- package/dist/commands/adoption.js +172 -0
- package/dist/commands/adoption.js.map +1 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.d.ts.map +1 -0
- package/dist/commands/analytics.js +7 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/commands/audit-verify.d.ts +3 -0
- package/dist/commands/audit-verify.d.ts.map +1 -0
- package/dist/commands/audit-verify.js +94 -0
- package/dist/commands/audit-verify.js.map +1 -0
- package/dist/commands/auto-setup.d.ts +28 -0
- package/dist/commands/auto-setup.d.ts.map +1 -0
- package/dist/commands/auto-setup.js +146 -0
- package/dist/commands/auto-setup.js.map +1 -0
- package/dist/commands/ci-check.d.ts +13 -0
- package/dist/commands/ci-check.d.ts.map +1 -0
- package/dist/commands/ci-check.js +286 -0
- package/dist/commands/ci-check.js.map +1 -0
- package/dist/commands/claude-hook.d.ts +2 -0
- package/dist/commands/claude-hook.d.ts.map +1 -0
- package/dist/commands/claude-hook.js +254 -0
- package/dist/commands/claude-hook.js.map +1 -0
- package/dist/commands/claude-init.d.ts.map +1 -0
- package/dist/commands/claude-init.js +401 -0
- package/dist/commands/claude-init.js.map +1 -0
- package/dist/commands/config.d.ts +39 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +368 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +100 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +364 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/evidence-pr.d.ts +3 -0
- package/dist/commands/evidence-pr.d.ts.map +1 -0
- package/dist/commands/evidence-pr.js +162 -0
- package/dist/commands/evidence-pr.js.map +1 -0
- package/dist/commands/export.d.ts +22 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +92 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/guard.d.ts +21 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +272 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/{cli/commands → commands}/import.js +25 -26
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1288 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect.d.ts +4 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/{cli/commands → commands}/inspect.js +97 -70
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/learn.d.ts +2 -0
- package/dist/commands/learn.d.ts.map +1 -0
- package/dist/commands/learn.js +264 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +270 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/{cli/commands → commands}/plugin.js +2 -2
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/policy-verify.d.ts +46 -0
- package/dist/commands/policy-verify.d.ts.map +1 -0
- package/dist/commands/policy-verify.js +173 -0
- package/dist/commands/policy-verify.js.map +1 -0
- package/dist/commands/policy.d.ts +27 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +590 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/{cli/commands → commands}/replay.d.ts +2 -1
- package/dist/commands/replay.d.ts.map +1 -0
- package/dist/{cli/commands → commands}/replay.js +78 -12
- package/dist/commands/replay.js.map +1 -0
- package/dist/commands/session-viewer.d.ts +5 -0
- package/dist/commands/session-viewer.d.ts.map +1 -0
- package/dist/commands/session-viewer.js +462 -0
- package/dist/commands/session-viewer.js.map +1 -0
- package/dist/commands/simulate.d.ts +7 -0
- package/dist/commands/simulate.d.ts.map +1 -0
- package/dist/commands/simulate.js +505 -0
- package/dist/commands/simulate.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +212 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/traces.d.ts +24 -0
- package/dist/commands/traces.d.ts.map +1 -0
- package/dist/commands/traces.js +282 -0
- package/dist/commands/traces.js.map +1 -0
- package/dist/commands/trust.d.ts +6 -0
- package/dist/commands/trust.d.ts.map +1 -0
- package/dist/commands/trust.js +129 -0
- package/dist/commands/trust.js.map +1 -0
- package/dist/evidence-summary.d.ts +35 -0
- package/dist/evidence-summary.d.ts.map +1 -0
- package/dist/evidence-summary.js +202 -0
- package/dist/evidence-summary.js.map +1 -0
- package/dist/{cli/file-event-store.d.ts → file-event-store.d.ts} +1 -1
- package/dist/file-event-store.d.ts.map +1 -0
- package/dist/{cli/file-event-store.js → file-event-store.js} +1 -1
- package/dist/file-event-store.js.map +1 -0
- package/{hooks → dist/hooks}/post-commit +3 -1
- package/{hooks → dist/hooks}/post-merge +3 -1
- package/dist/hooks/pre-commit +29 -0
- package/dist/policy-resolver.d.ts +32 -0
- package/dist/policy-resolver.d.ts.map +1 -0
- package/dist/policy-resolver.js +171 -0
- package/dist/policy-resolver.js.map +1 -0
- package/dist/{cli/recorder.d.ts → recorder.d.ts} +1 -1
- package/dist/recorder.d.ts.map +1 -0
- package/dist/{cli/recorder.js → recorder.js} +1 -1
- package/dist/recorder.js.map +1 -0
- package/dist/replay-timeline-html.d.ts +21 -0
- package/dist/replay-timeline-html.d.ts.map +1 -0
- package/dist/replay-timeline-html.js +414 -0
- package/dist/replay-timeline-html.js.map +1 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/{cli/replay.js → replay.js} +92 -1
- package/dist/replay.js.map +1 -0
- package/dist/{cli/session-store.d.ts → session-store.d.ts} +5 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/{cli/session-store.js → session-store.js} +1 -0
- package/dist/session-store.js.map +1 -0
- package/dist/session-viewer-html.d.ts +10 -0
- package/dist/session-viewer-html.d.ts.map +1 -0
- package/dist/session-viewer-html.js +737 -0
- package/dist/session-viewer-html.js.map +1 -0
- package/dist/templates/ci-only.yaml +103 -0
- package/dist/templates/development.yaml +107 -0
- package/dist/templates/permissive.yaml +87 -0
- package/dist/templates/strict.yaml +112 -0
- package/dist/tui.d.ts +3 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +2 -0
- package/dist/tui.js.map +1 -0
- package/package.json +42 -53
- package/dist/adapters/claude-code.d.ts +0 -24
- package/dist/adapters/claude-code.d.ts.map +0 -1
- package/dist/adapters/claude-code.js +0 -99
- package/dist/adapters/claude-code.js.map +0 -1
- package/dist/adapters/file.d.ts +0 -3
- package/dist/adapters/file.d.ts.map +0 -1
- package/dist/adapters/file.js +0 -35
- package/dist/adapters/file.js.map +0 -1
- package/dist/adapters/git.d.ts +0 -3
- package/dist/adapters/git.d.ts.map +0 -1
- package/dist/adapters/git.js +0 -61
- package/dist/adapters/git.js.map +0 -1
- package/dist/adapters/registry.d.ts +0 -5
- package/dist/adapters/registry.d.ts.map +0 -1
- package/dist/adapters/registry.js +0 -15
- package/dist/adapters/registry.js.map +0 -1
- package/dist/adapters/shell.d.ts +0 -8
- package/dist/adapters/shell.d.ts.map +0 -1
- package/dist/adapters/shell.js +0 -27
- package/dist/adapters/shell.js.map +0 -1
- package/dist/cli/args.d.ts.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/bin.d.ts.map +0 -1
- package/dist/cli/bin.js +0 -5769
- package/dist/cli/bin.js.map +0 -7
- package/dist/cli/colors.d.ts.map +0 -1
- package/dist/cli/colors.js.map +0 -1
- package/dist/cli/commands/claude-hook.d.ts +0 -2
- package/dist/cli/commands/claude-hook.d.ts.map +0 -1
- package/dist/cli/commands/claude-hook.js +0 -110
- package/dist/cli/commands/claude-hook.js.map +0 -1
- package/dist/cli/commands/claude-init.d.ts.map +0 -1
- package/dist/cli/commands/claude-init.js +0 -150
- package/dist/cli/commands/claude-init.js.map +0 -1
- package/dist/cli/commands/export.d.ts +0 -11
- package/dist/cli/commands/export.d.ts.map +0 -1
- package/dist/cli/commands/export.js +0 -113
- package/dist/cli/commands/export.js.map +0 -1
- package/dist/cli/commands/guard.d.ts +0 -12
- package/dist/cli/commands/guard.d.ts.map +0 -1
- package/dist/cli/commands/guard.js +0 -145
- package/dist/cli/commands/guard.js.map +0 -1
- package/dist/cli/commands/import.d.ts +0 -2
- package/dist/cli/commands/import.d.ts.map +0 -1
- package/dist/cli/commands/import.js.map +0 -1
- package/dist/cli/commands/inspect.d.ts +0 -3
- package/dist/cli/commands/inspect.d.ts.map +0 -1
- package/dist/cli/commands/inspect.js.map +0 -1
- package/dist/cli/commands/plugin.d.ts.map +0 -1
- package/dist/cli/commands/plugin.js.map +0 -1
- package/dist/cli/commands/replay.d.ts.map +0 -1
- package/dist/cli/commands/replay.js.map +0 -1
- package/dist/cli/file-event-store.d.ts.map +0 -1
- package/dist/cli/file-event-store.js.map +0 -1
- package/dist/cli/policy-resolver.d.ts +0 -4
- package/dist/cli/policy-resolver.d.ts.map +0 -1
- package/dist/cli/policy-resolver.js +0 -62
- package/dist/cli/policy-resolver.js.map +0 -1
- package/dist/cli/recorder.d.ts.map +0 -1
- package/dist/cli/recorder.js.map +0 -1
- package/dist/cli/replay.d.ts.map +0 -1
- package/dist/cli/replay.js.map +0 -1
- package/dist/cli/session-store.d.ts.map +0 -1
- package/dist/cli/session-store.js.map +0 -1
- package/dist/cli/tui.d.ts +0 -24
- package/dist/cli/tui.d.ts.map +0 -1
- package/dist/cli/tui.js +0 -197
- package/dist/cli/tui.js.map +0 -1
- package/dist/core/actions.d.ts +0 -11
- package/dist/core/actions.d.ts.map +0 -1
- package/dist/core/actions.js +0 -112
- package/dist/core/actions.js.map +0 -1
- package/dist/core/adapters.d.ts +0 -19
- package/dist/core/adapters.d.ts.map +0 -1
- package/dist/core/adapters.js +0 -85
- package/dist/core/adapters.js.map +0 -1
- package/dist/core/execution-log/bridge.d.ts +0 -12
- package/dist/core/execution-log/bridge.d.ts.map +0 -1
- package/dist/core/execution-log/bridge.js +0 -112
- package/dist/core/execution-log/bridge.js.map +0 -1
- package/dist/core/execution-log/event-log.d.ts +0 -7
- package/dist/core/execution-log/event-log.d.ts.map +0 -1
- package/dist/core/execution-log/event-log.js +0 -103
- package/dist/core/execution-log/event-log.js.map +0 -1
- package/dist/core/execution-log/event-projections.d.ts +0 -28
- package/dist/core/execution-log/event-projections.d.ts.map +0 -1
- package/dist/core/execution-log/event-projections.js +0 -272
- package/dist/core/execution-log/event-projections.js.map +0 -1
- package/dist/core/execution-log/event-schema.d.ts +0 -56
- package/dist/core/execution-log/event-schema.d.ts.map +0 -1
- package/dist/core/execution-log/event-schema.js +0 -160
- package/dist/core/execution-log/event-schema.js.map +0 -1
- package/dist/core/execution-log/index.d.ts +0 -7
- package/dist/core/execution-log/index.d.ts.map +0 -1
- package/dist/core/execution-log/index.js +0 -13
- package/dist/core/execution-log/index.js.map +0 -1
- package/dist/core/hash.d.ts +0 -5
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -13
- package/dist/core/hash.js.map +0 -1
- package/dist/core/rng.d.ts +0 -29
- package/dist/core/rng.d.ts.map +0 -1
- package/dist/core/rng.js +0 -48
- package/dist/core/rng.js.map +0 -1
- package/dist/core/types.d.ts +0 -746
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -8
- package/dist/core/types.js.map +0 -1
- package/dist/events/bus.d.ts +0 -24
- package/dist/events/bus.d.ts.map +0 -1
- package/dist/events/bus.js +0 -64
- package/dist/events/bus.js.map +0 -1
- package/dist/events/decision-jsonl.d.ts +0 -8
- package/dist/events/decision-jsonl.d.ts.map +0 -1
- package/dist/events/decision-jsonl.js +0 -44
- package/dist/events/decision-jsonl.js.map +0 -1
- package/dist/events/jsonl.d.ts +0 -8
- package/dist/events/jsonl.d.ts.map +0 -1
- package/dist/events/jsonl.js +0 -46
- package/dist/events/jsonl.js.map +0 -1
- package/dist/events/schema.d.ts +0 -59
- package/dist/events/schema.d.ts.map +0 -1
- package/dist/events/schema.js +0 -296
- package/dist/events/schema.js.map +0 -1
- package/dist/events/store.d.ts +0 -7
- package/dist/events/store.d.ts.map +0 -1
- package/dist/events/store.js +0 -64
- package/dist/events/store.js.map +0 -1
- package/dist/invariants/checker.d.ts +0 -15
- package/dist/invariants/checker.d.ts.map +0 -1
- package/dist/invariants/checker.js +0 -52
- package/dist/invariants/checker.js.map +0 -1
- package/dist/invariants/definitions.d.ts +0 -33
- package/dist/invariants/definitions.d.ts.map +0 -1
- package/dist/invariants/definitions.js +0 -168
- package/dist/invariants/definitions.js.map +0 -1
- package/dist/kernel/aab.d.ts +0 -26
- package/dist/kernel/aab.d.ts.map +0 -1
- package/dist/kernel/aab.js +0 -149
- package/dist/kernel/aab.js.map +0 -1
- package/dist/kernel/blast-radius.d.ts +0 -60
- package/dist/kernel/blast-radius.d.ts.map +0 -1
- package/dist/kernel/blast-radius.js +0 -146
- package/dist/kernel/blast-radius.js.map +0 -1
- package/dist/kernel/decision.d.ts +0 -40
- package/dist/kernel/decision.d.ts.map +0 -1
- package/dist/kernel/decision.js +0 -92
- package/dist/kernel/decision.js.map +0 -1
- package/dist/kernel/decisions/factory.d.ts +0 -12
- package/dist/kernel/decisions/factory.d.ts.map +0 -1
- package/dist/kernel/decisions/factory.js +0 -56
- package/dist/kernel/decisions/factory.js.map +0 -1
- package/dist/kernel/decisions/types.d.ts +0 -70
- package/dist/kernel/decisions/types.d.ts.map +0 -1
- package/dist/kernel/decisions/types.js +0 -5
- package/dist/kernel/decisions/types.js.map +0 -1
- package/dist/kernel/evidence.d.ts +0 -29
- package/dist/kernel/evidence.d.ts.map +0 -1
- package/dist/kernel/evidence.js +0 -61
- package/dist/kernel/evidence.js.map +0 -1
- package/dist/kernel/kernel.d.ts +0 -47
- package/dist/kernel/kernel.d.ts.map +0 -1
- package/dist/kernel/kernel.js +0 -377
- package/dist/kernel/kernel.js.map +0 -1
- package/dist/kernel/monitor.d.ts +0 -35
- package/dist/kernel/monitor.d.ts.map +0 -1
- package/dist/kernel/monitor.js +0 -144
- package/dist/kernel/monitor.js.map +0 -1
- package/dist/kernel/replay-comparator.d.ts +0 -72
- package/dist/kernel/replay-comparator.d.ts.map +0 -1
- package/dist/kernel/replay-comparator.js +0 -251
- package/dist/kernel/replay-comparator.js.map +0 -1
- package/dist/kernel/replay-engine.d.ts +0 -108
- package/dist/kernel/replay-engine.d.ts.map +0 -1
- package/dist/kernel/replay-engine.js +0 -241
- package/dist/kernel/replay-engine.js.map +0 -1
- package/dist/kernel/replay-processor.d.ts +0 -109
- package/dist/kernel/replay-processor.d.ts.map +0 -1
- package/dist/kernel/replay-processor.js +0 -118
- package/dist/kernel/replay-processor.js.map +0 -1
- package/dist/kernel/simulation/filesystem-simulator.d.ts +0 -3
- package/dist/kernel/simulation/filesystem-simulator.d.ts.map +0 -1
- package/dist/kernel/simulation/filesystem-simulator.js +0 -81
- package/dist/kernel/simulation/filesystem-simulator.js.map +0 -1
- package/dist/kernel/simulation/git-simulator.d.ts +0 -5
- package/dist/kernel/simulation/git-simulator.d.ts.map +0 -1
- package/dist/kernel/simulation/git-simulator.js +0 -115
- package/dist/kernel/simulation/git-simulator.js.map +0 -1
- package/dist/kernel/simulation/package-simulator.d.ts +0 -5
- package/dist/kernel/simulation/package-simulator.d.ts.map +0 -1
- package/dist/kernel/simulation/package-simulator.js +0 -164
- package/dist/kernel/simulation/package-simulator.js.map +0 -1
- package/dist/kernel/simulation/registry.d.ts +0 -3
- package/dist/kernel/simulation/registry.d.ts.map +0 -1
- package/dist/kernel/simulation/registry.js +0 -24
- package/dist/kernel/simulation/registry.js.map +0 -1
- package/dist/kernel/simulation/types.d.ts +0 -35
- package/dist/kernel/simulation/types.d.ts.map +0 -1
- package/dist/kernel/simulation/types.js +0 -4
- package/dist/kernel/simulation/types.js.map +0 -1
- package/dist/plugins/discovery.d.ts +0 -45
- package/dist/plugins/discovery.d.ts.map +0 -1
- package/dist/plugins/discovery.js +0 -89
- package/dist/plugins/discovery.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -10
- package/dist/plugins/index.d.ts.map +0 -1
- package/dist/plugins/index.js +0 -7
- package/dist/plugins/index.js.map +0 -1
- package/dist/plugins/registry.d.ts +0 -52
- package/dist/plugins/registry.d.ts.map +0 -1
- package/dist/plugins/registry.js +0 -148
- package/dist/plugins/registry.js.map +0 -1
- package/dist/plugins/sandbox.d.ts +0 -87
- package/dist/plugins/sandbox.d.ts.map +0 -1
- package/dist/plugins/sandbox.js +0 -122
- package/dist/plugins/sandbox.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -61
- package/dist/plugins/types.d.ts.map +0 -1
- package/dist/plugins/types.js +0 -16
- package/dist/plugins/types.js.map +0 -1
- package/dist/plugins/validator.d.ts +0 -36
- package/dist/plugins/validator.d.ts.map +0 -1
- package/dist/plugins/validator.js +0 -276
- package/dist/plugins/validator.js.map +0 -1
- package/dist/policy/evaluator.d.ts +0 -41
- package/dist/policy/evaluator.d.ts.map +0 -1
- package/dist/policy/evaluator.js +0 -111
- package/dist/policy/evaluator.js.map +0 -1
- package/dist/policy/loader.d.ts +0 -13
- package/dist/policy/loader.d.ts.map +0 -1
- package/dist/policy/loader.js +0 -118
- package/dist/policy/loader.js.map +0 -1
- package/dist/policy/pack-loader.d.ts +0 -40
- package/dist/policy/pack-loader.d.ts.map +0 -1
- package/dist/policy/pack-loader.js +0 -138
- package/dist/policy/pack-loader.js.map +0 -1
- package/dist/policy/yaml-loader.d.ts +0 -23
- package/dist/policy/yaml-loader.d.ts.map +0 -1
- package/dist/policy/yaml-loader.js +0 -222
- package/dist/policy/yaml-loader.js.map +0 -1
- package/dist/renderers/index.d.ts +0 -6
- package/dist/renderers/index.d.ts.map +0 -1
- package/dist/renderers/index.js +0 -4
- package/dist/renderers/index.js.map +0 -1
- package/dist/renderers/registry.d.ts +0 -39
- package/dist/renderers/registry.d.ts.map +0 -1
- package/dist/renderers/registry.js +0 -97
- package/dist/renderers/registry.js.map +0 -1
- package/dist/renderers/tui-renderer.d.ts +0 -18
- package/dist/renderers/tui-renderer.d.ts.map +0 -1
- package/dist/renderers/tui-renderer.js +0 -57
- package/dist/renderers/tui-renderer.js.map +0 -1
- package/dist/renderers/types.d.ts +0 -52
- package/dist/renderers/types.d.ts.map +0 -1
- package/dist/renderers/types.js +0 -4
- package/dist/renderers/types.js.map +0 -1
- package/dist/telemetry/index.d.ts +0 -3
- package/dist/telemetry/index.d.ts.map +0 -1
- package/dist/telemetry/index.js +0 -2
- package/dist/telemetry/index.js.map +0 -1
- package/dist/telemetry/runtimeLogger.d.ts +0 -9
- package/dist/telemetry/runtimeLogger.d.ts.map +0 -1
- package/dist/telemetry/runtimeLogger.js +0 -68
- package/dist/telemetry/runtimeLogger.js.map +0 -1
- package/dist/telemetry/types.d.ts +0 -22
- package/dist/telemetry/types.d.ts.map +0 -1
- package/dist/telemetry/types.js +0 -4
- package/dist/telemetry/types.js.map +0 -1
- package/hooks/pre-commit +0 -28
- /package/dist/{cli/args.d.ts → args.d.ts} +0 -0
- /package/dist/{cli/args.js → args.js} +0 -0
- /package/dist/{cli/bin.d.ts → bin.d.ts} +0 -0
- /package/dist/{cli/colors.d.ts → colors.d.ts} +0 -0
- /package/dist/{cli/colors.js → colors.js} +0 -0
- /package/dist/{cli/commands → commands}/claude-init.d.ts +0 -0
- /package/dist/{cli/commands → commands}/plugin.d.ts +0 -0
- /package/dist/{cli/replay.d.ts → replay.d.ts} +0 -0
|
@@ -0,0 +1,1288 @@
|
|
|
1
|
+
// CLI command: agentguard init — scaffold new governance extensions or policy templates.
|
|
2
|
+
//
|
|
3
|
+
// Generates boilerplate for extension types:
|
|
4
|
+
// invariant Custom invariant pack
|
|
5
|
+
// policy-pack Custom policy pack (YAML)
|
|
6
|
+
// adapter Custom execution adapter
|
|
7
|
+
// renderer Custom governance renderer
|
|
8
|
+
// replay-processor Custom replay processor
|
|
9
|
+
//
|
|
10
|
+
// Or scaffold a policy template:
|
|
11
|
+
// --template strict Maximum guardrails
|
|
12
|
+
// --template permissive Default-allow with safety nets
|
|
13
|
+
// --template ci-only Read-only CI pipeline mode
|
|
14
|
+
// --template development Balanced for active development
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { dirname, join, resolve } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { parseArgs } from '../args.js';
|
|
19
|
+
import { bold, color, dim } from '../colors.js';
|
|
20
|
+
const EXTENSION_TYPES = [
|
|
21
|
+
'invariant',
|
|
22
|
+
'policy-pack',
|
|
23
|
+
'adapter',
|
|
24
|
+
'renderer',
|
|
25
|
+
'replay-processor',
|
|
26
|
+
'simulator',
|
|
27
|
+
];
|
|
28
|
+
const TEMPLATE_NAMES = ['strict', 'permissive', 'ci-only', 'development'];
|
|
29
|
+
/**
|
|
30
|
+
* Main init command handler.
|
|
31
|
+
*/
|
|
32
|
+
export async function init(args) {
|
|
33
|
+
const parsed = parseArgs(args, {
|
|
34
|
+
string: ['--extension', '--name', '--dir', '--template', '--tiers'],
|
|
35
|
+
boolean: ['--force'],
|
|
36
|
+
alias: { '-e': '--extension', '-n': '--name', '-d': '--dir', '-t': '--template' },
|
|
37
|
+
});
|
|
38
|
+
const templateName = parsed.flags.template;
|
|
39
|
+
// Template mode: scaffold an agentguard.yaml from a built-in template
|
|
40
|
+
if (templateName) {
|
|
41
|
+
return initTemplate(templateName, parsed.flags.dir);
|
|
42
|
+
}
|
|
43
|
+
const extensionType = parsed.flags.extension ?? parsed.positional[0];
|
|
44
|
+
const name = parsed.flags.name;
|
|
45
|
+
const dir = parsed.flags.dir;
|
|
46
|
+
// Firestore setup mode
|
|
47
|
+
if (extensionType === 'firestore') {
|
|
48
|
+
return initFirestore(dir);
|
|
49
|
+
}
|
|
50
|
+
// Swarm scaffolding mode
|
|
51
|
+
if (extensionType === 'swarm') {
|
|
52
|
+
return initSwarm(parsed);
|
|
53
|
+
}
|
|
54
|
+
if (!extensionType) {
|
|
55
|
+
printInitHelp();
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
if (!isValidExtensionType(extensionType)) {
|
|
59
|
+
console.error(`\n ${color('Error', 'red')}: Unknown extension type "${extensionType}".`);
|
|
60
|
+
console.error(` Valid types: ${EXTENSION_TYPES.join(', ')}\n`);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
const extensionName = name ?? `my-${extensionType}`;
|
|
64
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(extensionName)) {
|
|
65
|
+
console.error(`\n ${color('Error', 'red')}: Invalid extension name "${extensionName}".`);
|
|
66
|
+
console.error(` Names must match /^[a-z0-9][a-z0-9-]*$/ to be safe for code generation.\n`);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
const targetDir = resolve(dir ?? extensionName);
|
|
70
|
+
if (existsSync(targetDir)) {
|
|
71
|
+
console.error(`\n ${color('Error', 'red')}: Directory "${targetDir}" already exists.`);
|
|
72
|
+
console.error(` Choose a different name with --name or a different path with --dir.\n`);
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const files = generateScaffold(extensionType, extensionName);
|
|
76
|
+
mkdirSync(targetDir, { recursive: true });
|
|
77
|
+
const srcDir = join(targetDir, 'src');
|
|
78
|
+
const testDir = join(targetDir, 'tests');
|
|
79
|
+
mkdirSync(srcDir, { recursive: true });
|
|
80
|
+
mkdirSync(testDir, { recursive: true });
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const fullPath = join(targetDir, file.path);
|
|
83
|
+
writeFileSync(fullPath, file.content, 'utf8');
|
|
84
|
+
}
|
|
85
|
+
console.log(`\n ${color('✓', 'green')} Scaffolded ${bold(extensionType)} extension: ${bold(extensionName)}\n`);
|
|
86
|
+
console.log(` ${bold('Files created:')}`);
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
console.log(` ${dim(file.path)}`);
|
|
89
|
+
}
|
|
90
|
+
console.log(`\n ${bold('Next steps:')}`);
|
|
91
|
+
console.log(` cd ${dir ?? extensionName}`);
|
|
92
|
+
console.log(` npm install`);
|
|
93
|
+
console.log(` # Edit src/index.ts to implement your extension`);
|
|
94
|
+
console.log(` agentguard plugin install .\n`);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
function isValidTemplateName(name) {
|
|
98
|
+
return TEMPLATE_NAMES.includes(name);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the templates directory. Uses the bundled templates/ directory
|
|
102
|
+
* relative to the project root (works both in dev and dist builds).
|
|
103
|
+
*/
|
|
104
|
+
function resolveTemplatesDir() {
|
|
105
|
+
// Walk up from this file to find the project root (where templates/ lives)
|
|
106
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
107
|
+
let dir = dirname(thisFile);
|
|
108
|
+
for (let i = 0; i < 5; i++) {
|
|
109
|
+
const candidate = join(dir, 'templates');
|
|
110
|
+
if (existsSync(candidate)) {
|
|
111
|
+
return candidate;
|
|
112
|
+
}
|
|
113
|
+
dir = dirname(dir);
|
|
114
|
+
}
|
|
115
|
+
return join(dirname(thisFile), '..', '..', '..', 'templates');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Scaffold an agentguard.yaml from a built-in policy template.
|
|
119
|
+
*/
|
|
120
|
+
function initTemplate(templateName, targetDir) {
|
|
121
|
+
if (!isValidTemplateName(templateName)) {
|
|
122
|
+
console.error(`\n ${color('Error', 'red')}: Unknown template "${templateName}".`);
|
|
123
|
+
console.error(` Available templates: ${TEMPLATE_NAMES.join(', ')}\n`);
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
const templatesDir = resolveTemplatesDir();
|
|
127
|
+
const templatePath = join(templatesDir, `${templateName}.yaml`);
|
|
128
|
+
if (!existsSync(templatePath)) {
|
|
129
|
+
console.error(`\n ${color('Error', 'red')}: Template file not found: ${templatePath}`);
|
|
130
|
+
console.error(` Ensure the templates/ directory is present in the AgentGuard installation.\n`);
|
|
131
|
+
return 1;
|
|
132
|
+
}
|
|
133
|
+
const outputDir = resolve(targetDir ?? '.');
|
|
134
|
+
const outputPath = join(outputDir, 'agentguard.yaml');
|
|
135
|
+
if (existsSync(outputPath)) {
|
|
136
|
+
console.error(`\n ${color('Error', 'red')}: ${outputPath} already exists.`);
|
|
137
|
+
console.error(` Remove or rename the existing file before scaffolding a template.\n`);
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
const content = readFileSync(templatePath, 'utf8');
|
|
141
|
+
writeFileSync(outputPath, content, 'utf8');
|
|
142
|
+
console.log(`\n ${color('✓', 'green')} Scaffolded ${bold(templateName)} policy template\n`);
|
|
143
|
+
console.log(` ${bold('File created:')}`);
|
|
144
|
+
console.log(` ${dim(outputPath)}\n`);
|
|
145
|
+
console.log(` ${bold('Next steps:')}`);
|
|
146
|
+
console.log(` # Review and customize the policy rules`);
|
|
147
|
+
console.log(` agentguard guard --policy agentguard.yaml --dry-run\n`);
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
function isValidExtensionType(type) {
|
|
151
|
+
return EXTENSION_TYPES.includes(type);
|
|
152
|
+
}
|
|
153
|
+
function generateScaffold(type, name) {
|
|
154
|
+
const id = `agentguard-${name}`;
|
|
155
|
+
switch (type) {
|
|
156
|
+
case 'invariant':
|
|
157
|
+
return scaffoldInvariant(id, name);
|
|
158
|
+
case 'policy-pack':
|
|
159
|
+
return scaffoldPolicyPack(id, name);
|
|
160
|
+
case 'adapter':
|
|
161
|
+
return scaffoldAdapter(id, name);
|
|
162
|
+
case 'renderer':
|
|
163
|
+
return scaffoldRenderer(id, name);
|
|
164
|
+
case 'replay-processor':
|
|
165
|
+
return scaffoldReplayProcessor(id, name);
|
|
166
|
+
case 'simulator':
|
|
167
|
+
return scaffoldSimulator(id, name);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Scaffold generators
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
function scaffoldInvariant(id, name) {
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
path: 'package.json',
|
|
177
|
+
content: JSON.stringify({
|
|
178
|
+
name: id,
|
|
179
|
+
version: '0.1.0',
|
|
180
|
+
description: `Custom invariant pack for AgentGuard`,
|
|
181
|
+
type: 'module',
|
|
182
|
+
main: 'src/index.js',
|
|
183
|
+
scripts: {
|
|
184
|
+
build: 'tsc',
|
|
185
|
+
test: 'node --test tests/',
|
|
186
|
+
},
|
|
187
|
+
agentguard: {
|
|
188
|
+
id,
|
|
189
|
+
name,
|
|
190
|
+
version: '0.1.0',
|
|
191
|
+
type: 'invariant',
|
|
192
|
+
apiVersion: '^1.0.0',
|
|
193
|
+
description: `Custom invariant pack: ${name}`,
|
|
194
|
+
},
|
|
195
|
+
}, null, 2) + '\n',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
path: 'tsconfig.json',
|
|
199
|
+
content: JSON.stringify({
|
|
200
|
+
compilerOptions: {
|
|
201
|
+
target: 'ES2022',
|
|
202
|
+
module: 'ESNext',
|
|
203
|
+
moduleResolution: 'bundler',
|
|
204
|
+
outDir: 'dist',
|
|
205
|
+
declaration: true,
|
|
206
|
+
strict: true,
|
|
207
|
+
verbatimModuleSyntax: true,
|
|
208
|
+
},
|
|
209
|
+
include: ['src'],
|
|
210
|
+
}, null, 2) + '\n',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
path: 'src/index.ts',
|
|
214
|
+
content: `// Custom invariant pack for AgentGuard
|
|
215
|
+
//
|
|
216
|
+
// Invariants are system-level checks that verify conditions hold before an
|
|
217
|
+
// action is allowed to execute. Each invariant has a check function that
|
|
218
|
+
// receives the current SystemState and returns whether the invariant holds.
|
|
219
|
+
//
|
|
220
|
+
// See: https://github.com/jpleva91/agent-guard#invariants
|
|
221
|
+
|
|
222
|
+
export interface InvariantCheckResult {
|
|
223
|
+
holds: boolean;
|
|
224
|
+
expected: string;
|
|
225
|
+
actual: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface SystemState {
|
|
229
|
+
modifiedFiles?: string[];
|
|
230
|
+
targetBranch?: string;
|
|
231
|
+
directPush?: boolean;
|
|
232
|
+
forcePush?: boolean;
|
|
233
|
+
isPush?: boolean;
|
|
234
|
+
testsPass?: boolean;
|
|
235
|
+
formatPass?: boolean;
|
|
236
|
+
filesAffected?: number;
|
|
237
|
+
blastRadiusLimit?: number;
|
|
238
|
+
protectedBranches?: string[];
|
|
239
|
+
simulatedBlastRadius?: number;
|
|
240
|
+
simulatedRiskLevel?: string;
|
|
241
|
+
currentTarget?: string;
|
|
242
|
+
currentCommand?: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface AgentGuardInvariant {
|
|
246
|
+
id: string;
|
|
247
|
+
name: string;
|
|
248
|
+
description: string;
|
|
249
|
+
severity: number;
|
|
250
|
+
check: (state: SystemState) => InvariantCheckResult;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// --- Your custom invariants ---
|
|
254
|
+
|
|
255
|
+
export const invariants: AgentGuardInvariant[] = [
|
|
256
|
+
{
|
|
257
|
+
id: '${id}-example',
|
|
258
|
+
name: 'Example Invariant',
|
|
259
|
+
description: 'Prevents modifications to files in the vendor/ directory',
|
|
260
|
+
severity: 3,
|
|
261
|
+
check(state) {
|
|
262
|
+
const vendorFiles = (state.modifiedFiles ?? []).filter((f) =>
|
|
263
|
+
f.startsWith('vendor/')
|
|
264
|
+
);
|
|
265
|
+
return {
|
|
266
|
+
holds: vendorFiles.length === 0,
|
|
267
|
+
expected: 'No vendor/ files modified',
|
|
268
|
+
actual:
|
|
269
|
+
vendorFiles.length === 0
|
|
270
|
+
? 'No vendor/ files modified'
|
|
271
|
+
: \`\${vendorFiles.length} vendor/ file(s) modified: \${vendorFiles.join(', ')}\`,
|
|
272
|
+
};
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
`,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
path: 'tests/invariant.test.ts',
|
|
280
|
+
content: `import { describe, it } from 'node:test';
|
|
281
|
+
import assert from 'node:assert/strict';
|
|
282
|
+
import { invariants } from '../src/index.js';
|
|
283
|
+
|
|
284
|
+
describe('${name} invariants', () => {
|
|
285
|
+
const inv = invariants[0];
|
|
286
|
+
|
|
287
|
+
it('should hold when no vendor files are modified', () => {
|
|
288
|
+
const result = inv.check({ modifiedFiles: ['src/app.ts'] });
|
|
289
|
+
assert.strictEqual(result.holds, true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should fail when vendor files are modified', () => {
|
|
293
|
+
const result = inv.check({ modifiedFiles: ['vendor/lib.js'] });
|
|
294
|
+
assert.strictEqual(result.holds, false);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
`,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
path: 'README.md',
|
|
301
|
+
content: `# ${name}
|
|
302
|
+
|
|
303
|
+
Custom invariant pack for AgentGuard.
|
|
304
|
+
|
|
305
|
+
## Usage
|
|
306
|
+
|
|
307
|
+
\`\`\`bash
|
|
308
|
+
agentguard plugin install .
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
## Invariants
|
|
312
|
+
|
|
313
|
+
| ID | Description | Severity |
|
|
314
|
+
|----|-------------|----------|
|
|
315
|
+
| ${id}-example | Prevents modifications to vendor/ files | 3 |
|
|
316
|
+
`,
|
|
317
|
+
},
|
|
318
|
+
];
|
|
319
|
+
}
|
|
320
|
+
function scaffoldPolicyPack(id, name) {
|
|
321
|
+
return [
|
|
322
|
+
{
|
|
323
|
+
path: 'package.json',
|
|
324
|
+
content: JSON.stringify({
|
|
325
|
+
name: id,
|
|
326
|
+
version: '0.1.0',
|
|
327
|
+
description: `Custom policy pack for AgentGuard`,
|
|
328
|
+
agentguard: {
|
|
329
|
+
id,
|
|
330
|
+
name,
|
|
331
|
+
version: '0.1.0',
|
|
332
|
+
type: 'policy-pack',
|
|
333
|
+
apiVersion: '^1.0.0',
|
|
334
|
+
description: `Custom policy pack: ${name}`,
|
|
335
|
+
},
|
|
336
|
+
}, null, 2) + '\n',
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
path: 'agentguard-pack.yaml',
|
|
340
|
+
content: `# ${name} — AgentGuard Policy Pack
|
|
341
|
+
#
|
|
342
|
+
# Reference this pack in your agentguard.yaml via:
|
|
343
|
+
# extends:
|
|
344
|
+
# - ./${name}
|
|
345
|
+
#
|
|
346
|
+
# Rules are evaluated in order. Deny rules take precedence over allow rules.
|
|
347
|
+
|
|
348
|
+
id: "${id}"
|
|
349
|
+
name: "${name}"
|
|
350
|
+
description: "Custom policy pack"
|
|
351
|
+
severity: 3
|
|
352
|
+
|
|
353
|
+
rules:
|
|
354
|
+
# Deny force-push to any branch
|
|
355
|
+
- action: "git.push"
|
|
356
|
+
effect: deny
|
|
357
|
+
branches: ["*"]
|
|
358
|
+
reason: "Force push is not allowed"
|
|
359
|
+
|
|
360
|
+
# Allow file reads in any scope
|
|
361
|
+
- action: "file.read"
|
|
362
|
+
effect: allow
|
|
363
|
+
|
|
364
|
+
# Allow test execution
|
|
365
|
+
- action:
|
|
366
|
+
- "test.run"
|
|
367
|
+
- "test.run.unit"
|
|
368
|
+
- "test.run.integration"
|
|
369
|
+
effect: allow
|
|
370
|
+
|
|
371
|
+
# Deny deploy without test pass
|
|
372
|
+
- action: "deploy.trigger"
|
|
373
|
+
effect: deny
|
|
374
|
+
requireTests: true
|
|
375
|
+
reason: "Tests must pass before deployment"
|
|
376
|
+
|
|
377
|
+
# Deny commit without format check
|
|
378
|
+
- action: "git.commit"
|
|
379
|
+
effect: deny
|
|
380
|
+
requireFormat: true
|
|
381
|
+
reason: "Formatting must pass before committing"
|
|
382
|
+
`,
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
path: 'README.md',
|
|
386
|
+
content: `# ${name}
|
|
387
|
+
|
|
388
|
+
Custom policy pack for AgentGuard.
|
|
389
|
+
|
|
390
|
+
## Usage
|
|
391
|
+
|
|
392
|
+
Add to your \`agentguard.yaml\`:
|
|
393
|
+
|
|
394
|
+
\`\`\`yaml
|
|
395
|
+
extends:
|
|
396
|
+
- ./${name}
|
|
397
|
+
\`\`\`
|
|
398
|
+
|
|
399
|
+
Or install as a plugin:
|
|
400
|
+
|
|
401
|
+
\`\`\`bash
|
|
402
|
+
agentguard plugin install .
|
|
403
|
+
\`\`\`
|
|
404
|
+
|
|
405
|
+
## Rules
|
|
406
|
+
|
|
407
|
+
| Action | Effect | Reason |
|
|
408
|
+
|--------|--------|--------|
|
|
409
|
+
| git.push | deny | Force push is not allowed |
|
|
410
|
+
| file.read | allow | — |
|
|
411
|
+
| test.* | allow | — |
|
|
412
|
+
| deploy.trigger | deny | Tests must pass before deployment |
|
|
413
|
+
`,
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
function scaffoldAdapter(id, name) {
|
|
418
|
+
return [
|
|
419
|
+
{
|
|
420
|
+
path: 'package.json',
|
|
421
|
+
content: JSON.stringify({
|
|
422
|
+
name: id,
|
|
423
|
+
version: '0.1.0',
|
|
424
|
+
description: `Custom execution adapter for AgentGuard`,
|
|
425
|
+
type: 'module',
|
|
426
|
+
main: 'src/index.js',
|
|
427
|
+
scripts: {
|
|
428
|
+
build: 'tsc',
|
|
429
|
+
test: 'node --test tests/',
|
|
430
|
+
},
|
|
431
|
+
agentguard: {
|
|
432
|
+
id,
|
|
433
|
+
name,
|
|
434
|
+
version: '0.1.0',
|
|
435
|
+
type: 'adapter',
|
|
436
|
+
apiVersion: '^1.0.0',
|
|
437
|
+
description: `Custom adapter: ${name}`,
|
|
438
|
+
capabilities: ['process:spawn'],
|
|
439
|
+
},
|
|
440
|
+
}, null, 2) + '\n',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
path: 'tsconfig.json',
|
|
444
|
+
content: JSON.stringify({
|
|
445
|
+
compilerOptions: {
|
|
446
|
+
target: 'ES2022',
|
|
447
|
+
module: 'ESNext',
|
|
448
|
+
moduleResolution: 'bundler',
|
|
449
|
+
outDir: 'dist',
|
|
450
|
+
declaration: true,
|
|
451
|
+
strict: true,
|
|
452
|
+
verbatimModuleSyntax: true,
|
|
453
|
+
},
|
|
454
|
+
include: ['src'],
|
|
455
|
+
}, null, 2) + '\n',
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
path: 'src/index.ts',
|
|
459
|
+
content: `// Custom execution adapter for AgentGuard
|
|
460
|
+
//
|
|
461
|
+
// Adapters translate authorized CanonicalAction objects into real operations.
|
|
462
|
+
// They are registered by action class (e.g., 'file', 'shell', 'git') and
|
|
463
|
+
// invoked by the kernel after an action passes policy + invariant checks.
|
|
464
|
+
//
|
|
465
|
+
// See: https://github.com/jpleva91/agent-guard#adapters
|
|
466
|
+
|
|
467
|
+
export interface CanonicalAction {
|
|
468
|
+
id: string;
|
|
469
|
+
type: string;
|
|
470
|
+
class: string;
|
|
471
|
+
target: string;
|
|
472
|
+
timestamp: number;
|
|
473
|
+
metadata?: Record<string, unknown>;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export type AdapterHandler = (action: CanonicalAction) => Promise<unknown> | unknown;
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Example adapter that handles a custom action class.
|
|
480
|
+
*
|
|
481
|
+
* Register this with the adapter registry:
|
|
482
|
+
* registry.register('custom', customAdapter);
|
|
483
|
+
*/
|
|
484
|
+
export const customAdapter: AdapterHandler = async (action) => {
|
|
485
|
+
switch (action.type) {
|
|
486
|
+
case 'custom.execute': {
|
|
487
|
+
// Implement your custom execution logic here
|
|
488
|
+
return { executed: true, target: action.target, timestamp: Date.now() };
|
|
489
|
+
}
|
|
490
|
+
default:
|
|
491
|
+
throw new Error(\`Unsupported action type: \${action.type}\`);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
`,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
path: 'tests/adapter.test.ts',
|
|
498
|
+
content: `import { describe, it } from 'node:test';
|
|
499
|
+
import assert from 'node:assert/strict';
|
|
500
|
+
import { customAdapter } from '../src/index.js';
|
|
501
|
+
|
|
502
|
+
describe('${name} adapter', () => {
|
|
503
|
+
it('should handle custom.execute actions', async () => {
|
|
504
|
+
const action = {
|
|
505
|
+
id: 'test-1',
|
|
506
|
+
type: 'custom.execute',
|
|
507
|
+
class: 'custom',
|
|
508
|
+
target: '/tmp/test',
|
|
509
|
+
timestamp: Date.now(),
|
|
510
|
+
};
|
|
511
|
+
const result = await customAdapter(action) as { executed: boolean };
|
|
512
|
+
assert.strictEqual(result.executed, true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should reject unsupported action types', async () => {
|
|
516
|
+
const action = {
|
|
517
|
+
id: 'test-2',
|
|
518
|
+
type: 'custom.unknown',
|
|
519
|
+
class: 'custom',
|
|
520
|
+
target: '/tmp/test',
|
|
521
|
+
timestamp: Date.now(),
|
|
522
|
+
};
|
|
523
|
+
await assert.rejects(() => Promise.resolve(customAdapter(action)));
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
`,
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
path: 'README.md',
|
|
530
|
+
content: `# ${name}
|
|
531
|
+
|
|
532
|
+
Custom execution adapter for AgentGuard.
|
|
533
|
+
|
|
534
|
+
## Usage
|
|
535
|
+
|
|
536
|
+
\`\`\`typescript
|
|
537
|
+
import { customAdapter } from '${id}';
|
|
538
|
+
|
|
539
|
+
registry.register('custom', customAdapter);
|
|
540
|
+
\`\`\`
|
|
541
|
+
|
|
542
|
+
## Supported Actions
|
|
543
|
+
|
|
544
|
+
| Type | Description |
|
|
545
|
+
|------|-------------|
|
|
546
|
+
| custom.execute | Execute a custom operation |
|
|
547
|
+
`,
|
|
548
|
+
},
|
|
549
|
+
];
|
|
550
|
+
}
|
|
551
|
+
function scaffoldRenderer(id, name) {
|
|
552
|
+
return [
|
|
553
|
+
{
|
|
554
|
+
path: 'package.json',
|
|
555
|
+
content: JSON.stringify({
|
|
556
|
+
name: id,
|
|
557
|
+
version: '0.1.0',
|
|
558
|
+
description: `Custom governance renderer for AgentGuard`,
|
|
559
|
+
type: 'module',
|
|
560
|
+
main: 'src/index.js',
|
|
561
|
+
scripts: {
|
|
562
|
+
build: 'tsc',
|
|
563
|
+
test: 'node --test tests/',
|
|
564
|
+
},
|
|
565
|
+
agentguard: {
|
|
566
|
+
id,
|
|
567
|
+
name,
|
|
568
|
+
version: '0.1.0',
|
|
569
|
+
type: 'renderer',
|
|
570
|
+
apiVersion: '^1.0.0',
|
|
571
|
+
description: `Custom renderer: ${name}`,
|
|
572
|
+
capabilities: ['filesystem:write'],
|
|
573
|
+
},
|
|
574
|
+
}, null, 2) + '\n',
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
path: 'tsconfig.json',
|
|
578
|
+
content: JSON.stringify({
|
|
579
|
+
compilerOptions: {
|
|
580
|
+
target: 'ES2022',
|
|
581
|
+
module: 'ESNext',
|
|
582
|
+
moduleResolution: 'bundler',
|
|
583
|
+
outDir: 'dist',
|
|
584
|
+
declaration: true,
|
|
585
|
+
strict: true,
|
|
586
|
+
verbatimModuleSyntax: true,
|
|
587
|
+
},
|
|
588
|
+
include: ['src'],
|
|
589
|
+
}, null, 2) + '\n',
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
path: 'src/index.ts',
|
|
593
|
+
content: `// Custom governance renderer for AgentGuard
|
|
594
|
+
//
|
|
595
|
+
// Renderers consume kernel results and produce output (terminal, file, JSON, etc.).
|
|
596
|
+
// They receive lifecycle callbacks as actions flow through the governance kernel.
|
|
597
|
+
// Multiple renderers can be active simultaneously.
|
|
598
|
+
//
|
|
599
|
+
// See: https://github.com/jpleva91/agent-guard#renderers
|
|
600
|
+
|
|
601
|
+
export interface RendererConfig {
|
|
602
|
+
readonly runId: string;
|
|
603
|
+
readonly policyName?: string;
|
|
604
|
+
readonly invariantCount?: number;
|
|
605
|
+
readonly verbose?: boolean;
|
|
606
|
+
readonly dryRun?: boolean;
|
|
607
|
+
readonly simulatorCount?: number;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export interface RunSummary {
|
|
611
|
+
readonly runId: string;
|
|
612
|
+
readonly totalActions: number;
|
|
613
|
+
readonly allowed: number;
|
|
614
|
+
readonly denied: number;
|
|
615
|
+
readonly violations: number;
|
|
616
|
+
readonly durationMs: number;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface GovernanceRenderer {
|
|
620
|
+
readonly id: string;
|
|
621
|
+
readonly name: string;
|
|
622
|
+
onRunStarted?(config: RendererConfig): void;
|
|
623
|
+
onActionResult?(result: unknown): void;
|
|
624
|
+
onRunEnded?(summary: RunSummary): void;
|
|
625
|
+
dispose?(): void;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** JSON file renderer — writes governance results to a JSON file. */
|
|
629
|
+
export function createJsonRenderer(outputPath: string): GovernanceRenderer {
|
|
630
|
+
const actions: unknown[] = [];
|
|
631
|
+
|
|
632
|
+
return {
|
|
633
|
+
id: '${id}',
|
|
634
|
+
name: '${name}',
|
|
635
|
+
|
|
636
|
+
onRunStarted(config) {
|
|
637
|
+
console.log(\`[\${this.name}] Run started: \${config.runId}\`);
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
onActionResult(result) {
|
|
641
|
+
actions.push(result);
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
onRunEnded(summary) {
|
|
645
|
+
const report = {
|
|
646
|
+
runId: summary.runId,
|
|
647
|
+
totalActions: summary.totalActions,
|
|
648
|
+
allowed: summary.allowed,
|
|
649
|
+
denied: summary.denied,
|
|
650
|
+
violations: summary.violations,
|
|
651
|
+
durationMs: summary.durationMs,
|
|
652
|
+
actions,
|
|
653
|
+
};
|
|
654
|
+
// In a real implementation, write to outputPath using fs.writeFileSync
|
|
655
|
+
console.log(\`[\${this.name}] Report: \${outputPath}\`);
|
|
656
|
+
console.log(JSON.stringify(report, null, 2));
|
|
657
|
+
},
|
|
658
|
+
|
|
659
|
+
dispose() {
|
|
660
|
+
actions.length = 0;
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
`,
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
path: 'tests/renderer.test.ts',
|
|
668
|
+
content: `import { describe, it } from 'node:test';
|
|
669
|
+
import assert from 'node:assert/strict';
|
|
670
|
+
import { createJsonRenderer } from '../src/index.js';
|
|
671
|
+
|
|
672
|
+
describe('${name} renderer', () => {
|
|
673
|
+
it('should create a renderer with correct id and name', () => {
|
|
674
|
+
const renderer = createJsonRenderer('/tmp/report.json');
|
|
675
|
+
assert.strictEqual(renderer.id, '${id}');
|
|
676
|
+
assert.strictEqual(renderer.name, '${name}');
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('should accept action results', () => {
|
|
680
|
+
const renderer = createJsonRenderer('/tmp/report.json');
|
|
681
|
+
assert.doesNotThrow(() => renderer.onActionResult?.({ type: 'test' }));
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
`,
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
path: 'README.md',
|
|
688
|
+
content: `# ${name}
|
|
689
|
+
|
|
690
|
+
Custom governance renderer for AgentGuard.
|
|
691
|
+
|
|
692
|
+
## Usage
|
|
693
|
+
|
|
694
|
+
\`\`\`bash
|
|
695
|
+
agentguard plugin install .
|
|
696
|
+
\`\`\`
|
|
697
|
+
|
|
698
|
+
## Features
|
|
699
|
+
|
|
700
|
+
- Outputs governance results as JSON
|
|
701
|
+
- Captures all action results during a run
|
|
702
|
+
- Produces a summary report at run end
|
|
703
|
+
`,
|
|
704
|
+
},
|
|
705
|
+
];
|
|
706
|
+
}
|
|
707
|
+
function scaffoldReplayProcessor(id, name) {
|
|
708
|
+
return [
|
|
709
|
+
{
|
|
710
|
+
path: 'package.json',
|
|
711
|
+
content: JSON.stringify({
|
|
712
|
+
name: id,
|
|
713
|
+
version: '0.1.0',
|
|
714
|
+
description: `Custom replay processor for AgentGuard`,
|
|
715
|
+
type: 'module',
|
|
716
|
+
main: 'src/index.js',
|
|
717
|
+
scripts: {
|
|
718
|
+
build: 'tsc',
|
|
719
|
+
test: 'node --test tests/',
|
|
720
|
+
},
|
|
721
|
+
agentguard: {
|
|
722
|
+
id,
|
|
723
|
+
name,
|
|
724
|
+
version: '0.1.0',
|
|
725
|
+
type: 'replay-processor',
|
|
726
|
+
apiVersion: '^1.0.0',
|
|
727
|
+
description: `Custom replay processor: ${name}`,
|
|
728
|
+
},
|
|
729
|
+
}, null, 2) + '\n',
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
path: 'tsconfig.json',
|
|
733
|
+
content: JSON.stringify({
|
|
734
|
+
compilerOptions: {
|
|
735
|
+
target: 'ES2022',
|
|
736
|
+
module: 'ESNext',
|
|
737
|
+
moduleResolution: 'bundler',
|
|
738
|
+
outDir: 'dist',
|
|
739
|
+
declaration: true,
|
|
740
|
+
strict: true,
|
|
741
|
+
verbatimModuleSyntax: true,
|
|
742
|
+
},
|
|
743
|
+
include: ['src'],
|
|
744
|
+
}, null, 2) + '\n',
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
path: 'src/index.ts',
|
|
748
|
+
content: `// Custom replay processor for AgentGuard
|
|
749
|
+
//
|
|
750
|
+
// Replay processors observe governance session replays and produce analytics.
|
|
751
|
+
// They receive lifecycle callbacks (session start/end, events, actions) and
|
|
752
|
+
// can accumulate results for downstream consumption.
|
|
753
|
+
//
|
|
754
|
+
// Processors are read-only observers — they must NOT mutate the session data.
|
|
755
|
+
//
|
|
756
|
+
// See: https://github.com/jpleva91/agent-guard#replay-processors
|
|
757
|
+
|
|
758
|
+
export interface ReplayProcessor {
|
|
759
|
+
readonly id: string;
|
|
760
|
+
readonly name: string;
|
|
761
|
+
readonly description?: string;
|
|
762
|
+
onSessionStart?(session: unknown): void | Promise<void>;
|
|
763
|
+
onEvent?(event: unknown): void | Promise<void>;
|
|
764
|
+
onAction?(action: ReplayAction): void | Promise<void>;
|
|
765
|
+
onSessionEnd?(session: unknown): void | Promise<void>;
|
|
766
|
+
getResults?(): Record<string, unknown>;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export interface ReplayAction {
|
|
770
|
+
id: string;
|
|
771
|
+
type: string;
|
|
772
|
+
target: string;
|
|
773
|
+
allowed: boolean;
|
|
774
|
+
reason?: string;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** Denial counter processor — counts denied actions during replay. */
|
|
778
|
+
export function createDenialCounter(): ReplayProcessor {
|
|
779
|
+
let totalActions = 0;
|
|
780
|
+
let deniedActions = 0;
|
|
781
|
+
const denialReasons = new Map<string, number>();
|
|
782
|
+
|
|
783
|
+
return {
|
|
784
|
+
id: '${id}',
|
|
785
|
+
name: '${name}',
|
|
786
|
+
description: 'Counts denied actions and groups by reason',
|
|
787
|
+
|
|
788
|
+
onAction(action) {
|
|
789
|
+
totalActions++;
|
|
790
|
+
if (!action.allowed) {
|
|
791
|
+
deniedActions++;
|
|
792
|
+
const reason = action.reason ?? 'unknown';
|
|
793
|
+
denialReasons.set(reason, (denialReasons.get(reason) ?? 0) + 1);
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
|
|
797
|
+
getResults() {
|
|
798
|
+
return {
|
|
799
|
+
totalActions,
|
|
800
|
+
deniedActions,
|
|
801
|
+
denialRate: totalActions > 0 ? deniedActions / totalActions : 0,
|
|
802
|
+
denialReasons: Object.fromEntries(denialReasons),
|
|
803
|
+
};
|
|
804
|
+
},
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
`,
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
path: 'tests/processor.test.ts',
|
|
811
|
+
content: `import { describe, it } from 'node:test';
|
|
812
|
+
import assert from 'node:assert/strict';
|
|
813
|
+
import { createDenialCounter } from '../src/index.js';
|
|
814
|
+
|
|
815
|
+
describe('${name} replay processor', () => {
|
|
816
|
+
it('should count denied actions', () => {
|
|
817
|
+
const processor = createDenialCounter();
|
|
818
|
+
processor.onAction?.({ id: '1', type: 'file.write', target: '/a', allowed: true });
|
|
819
|
+
processor.onAction?.({ id: '2', type: 'git.push', target: 'main', allowed: false, reason: 'policy' });
|
|
820
|
+
processor.onAction?.({ id: '3', type: 'git.push', target: 'main', allowed: false, reason: 'policy' });
|
|
821
|
+
|
|
822
|
+
const results = processor.getResults?.() as Record<string, unknown>;
|
|
823
|
+
assert.strictEqual(results.totalActions, 3);
|
|
824
|
+
assert.strictEqual(results.deniedActions, 2);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it('should group denials by reason', () => {
|
|
828
|
+
const processor = createDenialCounter();
|
|
829
|
+
processor.onAction?.({ id: '1', type: 'a', target: '/', allowed: false, reason: 'policy' });
|
|
830
|
+
processor.onAction?.({ id: '2', type: 'b', target: '/', allowed: false, reason: 'invariant' });
|
|
831
|
+
|
|
832
|
+
const results = processor.getResults?.() as Record<string, unknown>;
|
|
833
|
+
const reasons = results.denialReasons as Record<string, number>;
|
|
834
|
+
assert.strictEqual(reasons.policy, 1);
|
|
835
|
+
assert.strictEqual(reasons.invariant, 1);
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
`,
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
path: 'README.md',
|
|
842
|
+
content: `# ${name}
|
|
843
|
+
|
|
844
|
+
Custom replay processor for AgentGuard.
|
|
845
|
+
|
|
846
|
+
## Usage
|
|
847
|
+
|
|
848
|
+
\`\`\`bash
|
|
849
|
+
agentguard plugin install .
|
|
850
|
+
\`\`\`
|
|
851
|
+
|
|
852
|
+
## Features
|
|
853
|
+
|
|
854
|
+
- Counts denied vs allowed actions during session replay
|
|
855
|
+
- Groups denial reasons for pattern analysis
|
|
856
|
+
- Calculates denial rate metrics
|
|
857
|
+
`,
|
|
858
|
+
},
|
|
859
|
+
];
|
|
860
|
+
}
|
|
861
|
+
function scaffoldSimulator(id, name) {
|
|
862
|
+
return [
|
|
863
|
+
{
|
|
864
|
+
path: 'package.json',
|
|
865
|
+
content: JSON.stringify({
|
|
866
|
+
name: id,
|
|
867
|
+
version: '0.1.0',
|
|
868
|
+
description: `Custom action simulator for AgentGuard`,
|
|
869
|
+
type: 'module',
|
|
870
|
+
main: 'src/index.js',
|
|
871
|
+
scripts: {
|
|
872
|
+
build: 'tsc',
|
|
873
|
+
test: 'node --test tests/',
|
|
874
|
+
},
|
|
875
|
+
agentguard: {
|
|
876
|
+
id,
|
|
877
|
+
name,
|
|
878
|
+
version: '0.1.0',
|
|
879
|
+
type: 'simulator',
|
|
880
|
+
apiVersion: '^1.0.0',
|
|
881
|
+
description: `Custom simulator: ${name}`,
|
|
882
|
+
capabilities: ['filesystem:read'],
|
|
883
|
+
},
|
|
884
|
+
}, null, 2) + '\n',
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
path: 'tsconfig.json',
|
|
888
|
+
content: JSON.stringify({
|
|
889
|
+
compilerOptions: {
|
|
890
|
+
target: 'ES2022',
|
|
891
|
+
module: 'ESNext',
|
|
892
|
+
moduleResolution: 'bundler',
|
|
893
|
+
outDir: 'dist',
|
|
894
|
+
declaration: true,
|
|
895
|
+
strict: true,
|
|
896
|
+
verbatimModuleSyntax: true,
|
|
897
|
+
},
|
|
898
|
+
include: ['src'],
|
|
899
|
+
}, null, 2) + '\n',
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
path: 'src/index.ts',
|
|
903
|
+
content: `// Custom action simulator for AgentGuard
|
|
904
|
+
//
|
|
905
|
+
// Simulators predict the impact of an action BEFORE it executes. They are
|
|
906
|
+
// invoked by the governance kernel during the evaluate phase to produce
|
|
907
|
+
// structured impact forecasts used by predictive policy rules.
|
|
908
|
+
//
|
|
909
|
+
// Each simulator:
|
|
910
|
+
// 1. Declares which action types it supports via \`supports()\`
|
|
911
|
+
// 2. Returns a \`SimulationResult\` with predicted changes, blast radius, and risk
|
|
912
|
+
// 3. Is registered with the SimulatorRegistry at startup
|
|
913
|
+
//
|
|
914
|
+
// See: https://github.com/AgentGuardHQ/agent-guard#simulators
|
|
915
|
+
|
|
916
|
+
/** Normalized action intent passed to the simulator */
|
|
917
|
+
export interface NormalizedIntent {
|
|
918
|
+
action: string;
|
|
919
|
+
target?: string;
|
|
920
|
+
agent?: string;
|
|
921
|
+
destructive?: boolean;
|
|
922
|
+
command?: string;
|
|
923
|
+
filesAffected?: number;
|
|
924
|
+
metadata?: Record<string, unknown>;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/** Result of simulating an action before execution */
|
|
928
|
+
export interface SimulationResult {
|
|
929
|
+
/** Human-readable list of predicted changes */
|
|
930
|
+
predictedChanges: string[];
|
|
931
|
+
/** Estimated number of files/entities affected */
|
|
932
|
+
blastRadius: number;
|
|
933
|
+
/** Overall risk assessment */
|
|
934
|
+
riskLevel: 'low' | 'medium' | 'high';
|
|
935
|
+
/** Simulator-specific details */
|
|
936
|
+
details: Record<string, unknown>;
|
|
937
|
+
/** Which simulator produced this result */
|
|
938
|
+
simulatorId: string;
|
|
939
|
+
/** How long the simulation took (ms) */
|
|
940
|
+
durationMs: number;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** An action simulator predicts the impact of an action before execution */
|
|
944
|
+
export interface ActionSimulator {
|
|
945
|
+
/** Unique simulator identifier */
|
|
946
|
+
readonly id: string;
|
|
947
|
+
/** Check if this simulator can handle the given intent */
|
|
948
|
+
supports(intent: NormalizedIntent): boolean;
|
|
949
|
+
/** Simulate the action and predict its impact */
|
|
950
|
+
simulate(intent: NormalizedIntent, context: Record<string, unknown>): Promise<SimulationResult>;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// --- Supported action types for this simulator ---
|
|
954
|
+
|
|
955
|
+
const SUPPORTED_ACTIONS = new Set([
|
|
956
|
+
// Add the action types this simulator handles, e.g.:
|
|
957
|
+
// 'shell.exec',
|
|
958
|
+
// 'deploy.trigger',
|
|
959
|
+
]);
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Factory function — the plugin entry point.
|
|
963
|
+
*
|
|
964
|
+
* AgentGuard calls \`createSimulator()\` when loading your plugin.
|
|
965
|
+
* Return an object that implements the ActionSimulator interface.
|
|
966
|
+
*/
|
|
967
|
+
export function createSimulator(): ActionSimulator {
|
|
968
|
+
return {
|
|
969
|
+
id: '${id}',
|
|
970
|
+
|
|
971
|
+
supports(intent: NormalizedIntent): boolean {
|
|
972
|
+
return SUPPORTED_ACTIONS.has(intent.action);
|
|
973
|
+
},
|
|
974
|
+
|
|
975
|
+
async simulate(intent: NormalizedIntent): Promise<SimulationResult> {
|
|
976
|
+
const start = Date.now();
|
|
977
|
+
const target = intent.target ?? '';
|
|
978
|
+
|
|
979
|
+
// TODO: Implement your simulation logic here.
|
|
980
|
+
// Analyze the intent and predict what would happen if this action executes.
|
|
981
|
+
|
|
982
|
+
return {
|
|
983
|
+
predictedChanges: [\`Simulated: \${intent.action} on \${target}\`],
|
|
984
|
+
blastRadius: 1,
|
|
985
|
+
riskLevel: 'low',
|
|
986
|
+
details: {
|
|
987
|
+
target,
|
|
988
|
+
action: intent.action,
|
|
989
|
+
},
|
|
990
|
+
simulatorId: '${id}',
|
|
991
|
+
durationMs: Date.now() - start,
|
|
992
|
+
};
|
|
993
|
+
},
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
`,
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
path: 'tests/simulator.test.ts',
|
|
1000
|
+
content: `import { describe, it } from 'node:test';
|
|
1001
|
+
import assert from 'node:assert/strict';
|
|
1002
|
+
import { createSimulator } from '../src/index.js';
|
|
1003
|
+
|
|
1004
|
+
describe('${name} simulator', () => {
|
|
1005
|
+
const simulator = createSimulator();
|
|
1006
|
+
|
|
1007
|
+
it('should have a valid id', () => {
|
|
1008
|
+
assert.strictEqual(simulator.id, '${id}');
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
it('should report supported actions', () => {
|
|
1012
|
+
// Update this test when you add supported actions
|
|
1013
|
+
const result = simulator.supports({ action: 'file.read' });
|
|
1014
|
+
assert.strictEqual(typeof result, 'boolean');
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it('should return a valid SimulationResult', async () => {
|
|
1018
|
+
const result = await simulator.simulate(
|
|
1019
|
+
{ action: 'test.action', target: '/tmp/test' },
|
|
1020
|
+
{}
|
|
1021
|
+
);
|
|
1022
|
+
assert.strictEqual(typeof result.blastRadius, 'number');
|
|
1023
|
+
assert.ok(Array.isArray(result.predictedChanges));
|
|
1024
|
+
assert.ok(['low', 'medium', 'high'].includes(result.riskLevel));
|
|
1025
|
+
assert.strictEqual(result.simulatorId, '${id}');
|
|
1026
|
+
assert.strictEqual(typeof result.durationMs, 'number');
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
`,
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
path: 'README.md',
|
|
1033
|
+
content: `# ${name}
|
|
1034
|
+
|
|
1035
|
+
Custom action simulator for AgentGuard.
|
|
1036
|
+
|
|
1037
|
+
## Overview
|
|
1038
|
+
|
|
1039
|
+
This simulator predicts the impact of actions before they execute,
|
|
1040
|
+
enabling predictive governance decisions based on estimated blast radius
|
|
1041
|
+
and risk level.
|
|
1042
|
+
|
|
1043
|
+
## Usage
|
|
1044
|
+
|
|
1045
|
+
\`\`\`bash
|
|
1046
|
+
# Install as an AgentGuard plugin
|
|
1047
|
+
agentguard plugin install .
|
|
1048
|
+
|
|
1049
|
+
# The simulator is automatically loaded when running the guard
|
|
1050
|
+
agentguard guard --policy agentguard.yaml
|
|
1051
|
+
\`\`\`
|
|
1052
|
+
|
|
1053
|
+
## How It Works
|
|
1054
|
+
|
|
1055
|
+
1. The governance kernel calls \`supports(intent)\` to check if this simulator handles the action
|
|
1056
|
+
2. If supported, \`simulate(intent, context)\` predicts the impact
|
|
1057
|
+
3. The result feeds into the impact forecast and predictive policy rules
|
|
1058
|
+
|
|
1059
|
+
## Simulator Interface
|
|
1060
|
+
|
|
1061
|
+
| Method | Description |
|
|
1062
|
+
|--------|-------------|
|
|
1063
|
+
| \`supports(intent)\` | Returns true if this simulator handles the action type |
|
|
1064
|
+
| \`simulate(intent, context)\` | Returns predicted changes, blast radius, and risk level |
|
|
1065
|
+
|
|
1066
|
+
## Development
|
|
1067
|
+
|
|
1068
|
+
\`\`\`bash
|
|
1069
|
+
npm install
|
|
1070
|
+
npm run build
|
|
1071
|
+
npm test
|
|
1072
|
+
\`\`\`
|
|
1073
|
+
`,
|
|
1074
|
+
},
|
|
1075
|
+
];
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Scaffold Firestore backend configuration: security rules, env example, and setup guide.
|
|
1079
|
+
*/
|
|
1080
|
+
function initFirestore(targetDir) {
|
|
1081
|
+
const outputDir = resolve(targetDir ?? '.');
|
|
1082
|
+
// --- firestore.rules ---
|
|
1083
|
+
const rulesPath = join(outputDir, 'firestore.rules');
|
|
1084
|
+
if (existsSync(rulesPath)) {
|
|
1085
|
+
console.error(`\n ${color('Error', 'red')}: ${rulesPath} already exists.`);
|
|
1086
|
+
console.error(` Remove or rename the existing file before running init firestore.\n`);
|
|
1087
|
+
return 1;
|
|
1088
|
+
}
|
|
1089
|
+
const rulesContent = `rules_version = '2';
|
|
1090
|
+
service cloud.firestore {
|
|
1091
|
+
match /databases/{database}/documents {
|
|
1092
|
+
|
|
1093
|
+
// AgentGuard governance events — append-only for authenticated service accounts
|
|
1094
|
+
match /events/{eventId} {
|
|
1095
|
+
allow read: if request.auth != null;
|
|
1096
|
+
allow create: if request.auth != null
|
|
1097
|
+
&& request.resource.data.keys().hasAll(['id', 'run_id', 'kind', 'timestamp', 'data']);
|
|
1098
|
+
allow update, delete: if false; // Immutable audit trail
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// AgentGuard governance decisions — append-only
|
|
1102
|
+
match /decisions/{decisionId} {
|
|
1103
|
+
allow read: if request.auth != null;
|
|
1104
|
+
allow create: if request.auth != null
|
|
1105
|
+
&& request.resource.data.keys().hasAll(['record_id', 'run_id', 'outcome', 'timestamp', 'data']);
|
|
1106
|
+
allow update, delete: if false; // Immutable audit trail
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// Deny everything else by default
|
|
1110
|
+
match /{document=**} {
|
|
1111
|
+
allow read, write: if false;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
`;
|
|
1116
|
+
// --- .env.example ---
|
|
1117
|
+
const envPath = join(outputDir, '.env.firestore.example');
|
|
1118
|
+
const envContent = `# AgentGuard Firestore Configuration
|
|
1119
|
+
# Copy to .env and fill in your values.
|
|
1120
|
+
|
|
1121
|
+
# Required: GCP project ID
|
|
1122
|
+
GCLOUD_PROJECT=your-project-id
|
|
1123
|
+
|
|
1124
|
+
# Required for local dev / CI (not needed on GCP compute):
|
|
1125
|
+
# Path to a service account key with roles/datastore.user
|
|
1126
|
+
GOOGLE_APPLICATION_CREDENTIALS=/path/to/agentguard-sa.json
|
|
1127
|
+
|
|
1128
|
+
# Tell AgentGuard to use Firestore
|
|
1129
|
+
AGENTGUARD_STORE=firestore
|
|
1130
|
+
`;
|
|
1131
|
+
writeFileSync(rulesPath, rulesContent, 'utf8');
|
|
1132
|
+
writeFileSync(envPath, envContent, 'utf8');
|
|
1133
|
+
console.log(`\n ${color('✓', 'green')} Scaffolded ${bold('Firestore')} backend configuration\n`);
|
|
1134
|
+
console.log(` ${bold('Files created:')}`);
|
|
1135
|
+
console.log(` ${dim('firestore.rules')} — Deploy to Firestore to lock down access`);
|
|
1136
|
+
console.log(` ${dim('.env.firestore.example')} — Copy to .env with your GCP project ID\n`);
|
|
1137
|
+
console.log(` ${bold('Setup (GCP):')}`);
|
|
1138
|
+
console.log(` ${dim('# 1. Create a service account with minimal permissions')}`);
|
|
1139
|
+
console.log(` gcloud iam service-accounts create agentguard-writer \\`);
|
|
1140
|
+
console.log(` --display-name="AgentGuard Event Writer"`);
|
|
1141
|
+
console.log();
|
|
1142
|
+
console.log(` ${dim('# 2. Grant only datastore.user (read/write docs, no admin)')}`);
|
|
1143
|
+
console.log(` gcloud projects add-iam-policy-binding $GCLOUD_PROJECT \\`);
|
|
1144
|
+
console.log(` --member="serviceAccount:agentguard-writer@$GCLOUD_PROJECT.iam.gserviceaccount.com" \\`);
|
|
1145
|
+
console.log(` --role="roles/datastore.user"`);
|
|
1146
|
+
console.log();
|
|
1147
|
+
console.log(` ${dim('# 3. Generate a key (never commit this)')}`);
|
|
1148
|
+
console.log(` gcloud iam service-accounts keys create agentguard-sa.json \\`);
|
|
1149
|
+
console.log(` --iam-account=agentguard-writer@$GCLOUD_PROJECT.iam.gserviceaccount.com`);
|
|
1150
|
+
console.log();
|
|
1151
|
+
console.log(` ${dim('# 4. Deploy security rules')}`);
|
|
1152
|
+
console.log(` firebase deploy --only firestore:rules`);
|
|
1153
|
+
console.log();
|
|
1154
|
+
console.log(` ${dim('# 5. Run AgentGuard with Firestore')}`);
|
|
1155
|
+
console.log(` agentguard guard --store firestore --policy agentguard.yaml\n`);
|
|
1156
|
+
return 0;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Scaffold the agent swarm: copy skill templates, render config, and output
|
|
1160
|
+
* scheduled task definitions for registration.
|
|
1161
|
+
*/
|
|
1162
|
+
async function initSwarm(parsed) {
|
|
1163
|
+
const dir = parsed.flags.dir;
|
|
1164
|
+
const force = parsed.flags.force === true || parsed.flags.force === 'true';
|
|
1165
|
+
const tiersFlag = parsed.flags.tiers;
|
|
1166
|
+
const tiers = tiersFlag ? tiersFlag.split(',').map((t) => t.trim()) : undefined;
|
|
1167
|
+
const projectRoot = resolve(dir ?? '.');
|
|
1168
|
+
let scaffoldFn;
|
|
1169
|
+
try {
|
|
1170
|
+
const swarmModule = await import('@red-codes/swarm');
|
|
1171
|
+
scaffoldFn = swarmModule.scaffold;
|
|
1172
|
+
}
|
|
1173
|
+
catch {
|
|
1174
|
+
console.error(`\n ${color('Error', 'red')}: @red-codes/swarm package not found.`);
|
|
1175
|
+
console.error(` Install it with: pnpm add @red-codes/swarm\n`);
|
|
1176
|
+
return 1;
|
|
1177
|
+
}
|
|
1178
|
+
const result = scaffoldFn({ projectRoot, force, tiers });
|
|
1179
|
+
console.log(`\n ${color('✓', 'green')} Swarm initialized (${bold(String(result.agents.length))} agents, ${bold(String(result.skillsWritten + result.skillsSkipped))} skills)\n`);
|
|
1180
|
+
if (result.configWritten) {
|
|
1181
|
+
console.log(` ${dim('Created')} agentguard-swarm.yaml ${dim('(customize schedules, paths, labels)')}`);
|
|
1182
|
+
}
|
|
1183
|
+
console.log(` ${dim('Skills written:')} ${result.skillsWritten} ${dim('Skipped (existing):')} ${result.skillsSkipped}\n`);
|
|
1184
|
+
// Print agent table
|
|
1185
|
+
console.log(` ${bold('Agent')}${' '.repeat(28)}${bold('Tier')}${' '.repeat(8)}${bold('Schedule')}`);
|
|
1186
|
+
console.log(` ${'─'.repeat(65)}`);
|
|
1187
|
+
for (const agent of result.agents) {
|
|
1188
|
+
const name = agent.name.padEnd(33);
|
|
1189
|
+
const tier = agent.tier.padEnd(12);
|
|
1190
|
+
console.log(` ${name}${tier}${agent.cron}`);
|
|
1191
|
+
}
|
|
1192
|
+
console.log(`\n ${bold('Next steps:')}`);
|
|
1193
|
+
console.log(` ${dim('# Register scheduled tasks (run inside Claude Code):')}`);
|
|
1194
|
+
console.log(` ${dim('# The agent prompts are in .claude/skills/ — use them with the scheduled tasks API')}`);
|
|
1195
|
+
console.log(` ${dim('# Or use the register-swarm-tasks skill to auto-register all agents')}\n`);
|
|
1196
|
+
// Write a register-swarm-tasks skill
|
|
1197
|
+
const registerSkillPath = join(projectRoot, '.claude', 'skills', 'register-swarm-tasks.md');
|
|
1198
|
+
if (!existsSync(registerSkillPath) || force) {
|
|
1199
|
+
const registerContent = buildRegisterSkill(result);
|
|
1200
|
+
mkdirSync(join(projectRoot, '.claude', 'skills'), { recursive: true });
|
|
1201
|
+
writeFileSync(registerSkillPath, registerContent, 'utf8');
|
|
1202
|
+
console.log(` ${dim('Created')} .claude/skills/register-swarm-tasks.md\n`);
|
|
1203
|
+
}
|
|
1204
|
+
return 0;
|
|
1205
|
+
}
|
|
1206
|
+
function buildRegisterSkill(result) {
|
|
1207
|
+
const lines = [
|
|
1208
|
+
'# Skill: Register Swarm Tasks',
|
|
1209
|
+
'',
|
|
1210
|
+
'Register all swarm agents as scheduled tasks. Run this once after `agentguard init swarm`.',
|
|
1211
|
+
'',
|
|
1212
|
+
'## Autonomy Directive',
|
|
1213
|
+
'',
|
|
1214
|
+
'This skill runs interactively. Confirm with the user before creating tasks.',
|
|
1215
|
+
'',
|
|
1216
|
+
'## Steps',
|
|
1217
|
+
'',
|
|
1218
|
+
'### 1. Create Scheduled Tasks',
|
|
1219
|
+
'',
|
|
1220
|
+
'Use the `mcp__scheduled-tasks__create_scheduled_task` tool to register each agent:',
|
|
1221
|
+
'',
|
|
1222
|
+
];
|
|
1223
|
+
for (const agent of result.agents) {
|
|
1224
|
+
lines.push(`#### ${agent.name}`);
|
|
1225
|
+
lines.push('');
|
|
1226
|
+
lines.push(`- **Task ID**: \`${agent.id}\``);
|
|
1227
|
+
lines.push(`- **Cron**: \`${agent.cron}\``);
|
|
1228
|
+
lines.push(`- **Description**: ${agent.description}`);
|
|
1229
|
+
lines.push(`- **Prompt**: Use the content from the \`${agent.id}\` prompt template`);
|
|
1230
|
+
lines.push('');
|
|
1231
|
+
}
|
|
1232
|
+
lines.push('### 2. Verify');
|
|
1233
|
+
lines.push('');
|
|
1234
|
+
lines.push('After creating all tasks, use `mcp__scheduled-tasks__list_scheduled_tasks` to verify they are registered.');
|
|
1235
|
+
lines.push('');
|
|
1236
|
+
return lines.join('\n');
|
|
1237
|
+
}
|
|
1238
|
+
function printInitHelp() {
|
|
1239
|
+
console.log(`
|
|
1240
|
+
${bold('agentguard init')} — Scaffold a new governance extension, policy template, or agent swarm
|
|
1241
|
+
|
|
1242
|
+
${bold('Usage:')}
|
|
1243
|
+
agentguard init --extension <type> [--name <name>] [--dir <path>]
|
|
1244
|
+
agentguard init --template <name> [--dir <path>]
|
|
1245
|
+
agentguard init <type> [--name <name>] [--dir <path>]
|
|
1246
|
+
|
|
1247
|
+
${bold('Extension types:')}
|
|
1248
|
+
invariant Custom invariant pack
|
|
1249
|
+
policy-pack Custom policy pack (YAML rules)
|
|
1250
|
+
adapter Custom execution adapter
|
|
1251
|
+
renderer Custom governance renderer
|
|
1252
|
+
replay-processor Custom replay processor
|
|
1253
|
+
simulator Custom action simulator
|
|
1254
|
+
|
|
1255
|
+
${bold('Agent swarm:')}
|
|
1256
|
+
swarm Scaffold the full agent swarm (skills, config, task definitions)
|
|
1257
|
+
|
|
1258
|
+
${bold('Storage backends:')}
|
|
1259
|
+
firestore Set up Firestore backend (security rules + credentials guide)
|
|
1260
|
+
|
|
1261
|
+
${bold('Policy templates:')}
|
|
1262
|
+
strict Maximum guardrails — deny all destructive ops
|
|
1263
|
+
permissive Default-allow with safety nets for dangerous ops
|
|
1264
|
+
ci-only Read-only CI pipeline mode — build and test only
|
|
1265
|
+
development Balanced guardrails for active development
|
|
1266
|
+
|
|
1267
|
+
${bold('Flags:')}
|
|
1268
|
+
--extension, -e Extension type
|
|
1269
|
+
--template, -t Policy template name (creates agentguard.yaml)
|
|
1270
|
+
--name, -n Extension name (default: my-<type>)
|
|
1271
|
+
--dir, -d Output directory (default: ./<name> or . for templates)
|
|
1272
|
+
--tiers Comma-separated tiers for swarm (core,governance,ops,quality,marketing)
|
|
1273
|
+
--force Overwrite existing skill files during swarm init
|
|
1274
|
+
|
|
1275
|
+
${bold('Examples:')}
|
|
1276
|
+
agentguard init --template strict
|
|
1277
|
+
agentguard init --template development --dir ./my-project
|
|
1278
|
+
agentguard init --extension renderer --name json-renderer
|
|
1279
|
+
agentguard init invariant --name vendor-guard
|
|
1280
|
+
agentguard init policy-pack --name strict-policy
|
|
1281
|
+
agentguard init simulator --name docker-build
|
|
1282
|
+
agentguard init firestore
|
|
1283
|
+
agentguard init swarm
|
|
1284
|
+
agentguard init swarm --tiers core,governance
|
|
1285
|
+
agentguard init swarm --force
|
|
1286
|
+
`);
|
|
1287
|
+
}
|
|
1288
|
+
//# sourceMappingURL=init.js.map
|