@guava-parity/guard-scanner 13.0.0 → 16.0.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.
Files changed (96) hide show
  1. package/README.md +170 -215
  2. package/README_ja.md +252 -0
  3. package/SECURITY.md +12 -4
  4. package/SKILL.md +148 -57
  5. package/dist/cli.cjs +5997 -0
  6. package/dist/cli.d.mts +1 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.mjs +6003 -0
  9. package/dist/index.cjs +4825 -0
  10. package/dist/index.d.mts +17 -0
  11. package/dist/index.d.ts +17 -0
  12. package/dist/index.mjs +4798 -0
  13. package/dist/mcp-server.cjs +4756 -0
  14. package/dist/mcp-server.d.mts +1 -0
  15. package/dist/mcp-server.d.ts +1 -0
  16. package/dist/mcp-server.mjs +4767 -0
  17. package/dist/openclaw-plugin.cjs +4863 -0
  18. package/dist/openclaw-plugin.d.mts +11 -0
  19. package/dist/openclaw-plugin.d.ts +11 -0
  20. package/dist/openclaw-plugin.mjs +4854 -0
  21. package/dist/types.cjs +18 -0
  22. package/dist/types.d.mts +215 -0
  23. package/dist/types.d.ts +215 -0
  24. package/dist/types.mjs +1 -0
  25. package/docs/EVIDENCE_DRIVEN.md +182 -0
  26. package/docs/banner.png +0 -0
  27. package/docs/data/benchmark-ledger.json +1428 -0
  28. package/docs/data/corpus-metrics.json +11 -0
  29. package/docs/data/fp-ledger.json +18 -0
  30. package/docs/data/latest.json +25837 -2481
  31. package/docs/data/quality-contract.json +36 -0
  32. package/docs/generated/npm-audit-20260312.json +96 -0
  33. package/docs/generated/openclaw-upstream-status.json +25 -0
  34. package/docs/glossary.md +46 -0
  35. package/docs/index.html +1085 -496
  36. package/docs/logo.png +0 -0
  37. package/docs/openclaw-compatibility-audit.md +45 -0
  38. package/docs/openclaw-continuous-compatibility-plan.md +37 -0
  39. package/docs/rules/a2a-contagion.md +68 -0
  40. package/docs/rules/advanced-exfil.md +52 -0
  41. package/docs/rules/agent-protocol.md +108 -0
  42. package/docs/rules/api-abuse.md +68 -0
  43. package/docs/rules/autonomous-risk.md +92 -0
  44. package/docs/rules/config-impact.md +132 -0
  45. package/docs/rules/credential-handling.md +100 -0
  46. package/docs/rules/cve-patterns.md +332 -0
  47. package/docs/rules/data-exposure.md +84 -0
  48. package/docs/rules/exfiltration.md +36 -0
  49. package/docs/rules/financial-access.md +84 -0
  50. package/docs/rules/identity-hijack.md +140 -0
  51. package/docs/rules/inference-manipulation.md +60 -0
  52. package/docs/rules/leaky-skills.md +52 -0
  53. package/docs/rules/malicious-code.md +108 -0
  54. package/docs/rules/mcp-security.md +148 -0
  55. package/docs/rules/memory-poisoning.md +84 -0
  56. package/docs/rules/model-poisoning.md +44 -0
  57. package/docs/rules/obfuscation.md +60 -0
  58. package/docs/rules/persistence.md +108 -0
  59. package/docs/rules/pii-exposure.md +116 -0
  60. package/docs/rules/prompt-injection.md +148 -0
  61. package/docs/rules/prompt-worm.md +44 -0
  62. package/docs/rules/safeguard-bypass.md +44 -0
  63. package/docs/rules/sandbox-escape.md +100 -0
  64. package/docs/rules/secret-detection.md +44 -0
  65. package/docs/rules/supply-chain-v2.md +92 -0
  66. package/docs/rules/suspicious-download.md +60 -0
  67. package/docs/rules/trust-boundary.md +76 -0
  68. package/docs/rules/trust-exploitation.md +92 -0
  69. package/docs/rules/unverifiable-deps.md +84 -0
  70. package/docs/rules/vdb-injection.md +84 -0
  71. package/docs/security-vulnerability-report-20260312.md +53 -0
  72. package/docs/spec/PRD_V2_ARCHITECTURE.md +55 -0
  73. package/docs/spec/capabilities.json +174 -0
  74. package/docs/spec/finding.schema.json +104 -0
  75. package/docs/spec/integration-manifest.md +39 -0
  76. package/docs/spec/plugin-trust.json +11 -0
  77. package/docs/spec/sbom.json +33 -0
  78. package/docs/threat-model.md +65 -0
  79. package/docs/v13-architecture-manifest.md +55 -0
  80. package/hooks/context.ts +306 -0
  81. package/hooks/guard-scanner/plugin.ts +24 -1
  82. package/openclaw-plugin.mts +107 -0
  83. package/openclaw.plugin.json +30 -53
  84. package/package.json +66 -13
  85. package/src/asset-auditor.js +0 -508
  86. package/src/ci-reporter.js +0 -135
  87. package/src/cli.js +0 -294
  88. package/src/html-template.js +0 -239
  89. package/src/ioc-db.js +0 -54
  90. package/src/mcp-server.js +0 -702
  91. package/src/patterns.js +0 -611
  92. package/src/quarantine.js +0 -41
  93. package/src/runtime-guard.js +0 -346
  94. package/src/scanner.js +0 -1157
  95. package/src/vt-client.js +0 -202
  96. package/src/watcher.js +0 -170
package/dist/types.cjs ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
@@ -0,0 +1,215 @@
1
+ type Severity = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
2
+ type GuardMode = "monitor" | "enforce" | "strict";
3
+ type RuntimeAction = "blocked" | "warned";
4
+ interface EvidenceSpan {
5
+ file?: string;
6
+ start_line: number;
7
+ end_line: number;
8
+ }
9
+ interface FindingEvidence {
10
+ file?: string;
11
+ line?: number | null;
12
+ sample?: string;
13
+ match_count?: number;
14
+ tool_name?: string;
15
+ params_preview?: string;
16
+ layer?: number;
17
+ layer_name?: string;
18
+ owasp_asi?: string[];
19
+ protocol_surface?: string[];
20
+ }
21
+ interface Finding {
22
+ schema_version?: string;
23
+ source?: "static" | "runtime";
24
+ id: string;
25
+ rule_id?: string;
26
+ cat?: string;
27
+ category: string;
28
+ severity: Severity;
29
+ desc?: string;
30
+ description: string;
31
+ file?: string;
32
+ line?: number | null;
33
+ matchCount?: number;
34
+ sample?: string;
35
+ rationale: string;
36
+ preconditions: string;
37
+ remediation_hint: string;
38
+ false_positive_scenarios: string[];
39
+ validation_state: string;
40
+ validation_status: string;
41
+ confidence: number;
42
+ attack_chain_id: string | null;
43
+ evidence: FindingEvidence;
44
+ evidence_spans: EvidenceSpan[];
45
+ layer?: number;
46
+ layer_name?: string;
47
+ owasp_asi?: string[];
48
+ protocol_surface?: string[];
49
+ action?: RuntimeAction;
50
+ }
51
+ interface SkillFindingResult {
52
+ skill: string;
53
+ risk: number;
54
+ verdict: string;
55
+ findings: Finding[];
56
+ }
57
+ interface ThresholdBand {
58
+ suspicious: number;
59
+ malicious: number;
60
+ }
61
+ interface ScanStats {
62
+ scanned: number;
63
+ clean: number;
64
+ low: number;
65
+ suspicious: number;
66
+ malicious: number;
67
+ }
68
+ interface Recommendation {
69
+ skill: string;
70
+ actions: string[];
71
+ }
72
+ interface ScanReport {
73
+ schema_version: string;
74
+ timestamp: string;
75
+ scanner: string;
76
+ finding_schema_version: string;
77
+ mode: "normal" | "strict";
78
+ compliance_mode?: "owasp-asi" | null;
79
+ stats: ScanStats;
80
+ thresholds: ThresholdBand;
81
+ findings: SkillFindingResult[];
82
+ recommendations: Recommendation[];
83
+ layer_summary?: Array<Record<string, unknown>>;
84
+ owasp_asi_coverage?: Array<Record<string, unknown>>;
85
+ threat_model?: Record<string, unknown>;
86
+ iocVersion: string;
87
+ }
88
+ interface TextScanResult {
89
+ safe: boolean;
90
+ risk: number;
91
+ detections: Finding[];
92
+ }
93
+ interface ScannerOptions {
94
+ verbose?: boolean;
95
+ selfExclude?: boolean;
96
+ strict?: boolean;
97
+ summaryOnly?: boolean;
98
+ quiet?: boolean;
99
+ checkDeps?: boolean;
100
+ soulLock?: boolean;
101
+ plugins?: string[];
102
+ rulesFile?: string;
103
+ compliance?: "owasp-asi";
104
+ }
105
+ interface CustomRule {
106
+ id: string;
107
+ cat: string;
108
+ regex: RegExp;
109
+ severity: Severity;
110
+ desc: string;
111
+ codeOnly?: boolean;
112
+ docOnly?: boolean;
113
+ all?: boolean;
114
+ soulLock?: boolean;
115
+ }
116
+ interface PluginConfig {
117
+ mode?: GuardMode;
118
+ auditLog?: boolean;
119
+ customRules?: string;
120
+ }
121
+ interface RuntimeDecision {
122
+ blocked: boolean;
123
+ blockReason: string | null;
124
+ detections: Finding[];
125
+ mode: GuardMode;
126
+ toolName?: string;
127
+ matchedPolicyId?: string | null;
128
+ policyRationale?: string | null;
129
+ riskAmplificationReasons?: string[];
130
+ remediationSuggestion?: string | null;
131
+ policyDecision?: RuntimePolicyDecision | null;
132
+ }
133
+ interface McpRequest {
134
+ method: string;
135
+ params?: Record<string, unknown>;
136
+ id?: string | number | null;
137
+ }
138
+ interface SarifReport {
139
+ version: string;
140
+ $schema?: string;
141
+ runs: Array<Record<string, unknown>>;
142
+ }
143
+ interface CapabilityMetrics {
144
+ static_pattern_count: number;
145
+ runtime_check_count?: number;
146
+ threat_category_count: number;
147
+ runtime_layer_count?: number;
148
+ runtime_layers?: number;
149
+ benchmark_corpus_version?: string;
150
+ explainability_completeness_rate?: number;
151
+ runtime_check_latency_budget_ms?: number;
152
+ quality_targets?: QualityTargets;
153
+ [key: string]: unknown;
154
+ }
155
+ interface RuntimeCheckStats {
156
+ total: number;
157
+ byLayer: Record<number, number>;
158
+ bySeverity: Partial<Record<Severity, number>>;
159
+ }
160
+ interface QualityTargets {
161
+ precision_min: number;
162
+ recall_min: number;
163
+ false_positive_rate_max: number;
164
+ false_negative_rate_max: number;
165
+ explainability_completeness_rate_min: number;
166
+ runtime_check_latency_budget_ms: number;
167
+ false_positive_budget_by_category: Record<string, number>;
168
+ }
169
+ interface RuntimePolicyContract {
170
+ id?: string;
171
+ allowed_tools?: string[];
172
+ blocked_tools?: string[];
173
+ max_network_scope?: "none" | "internal-only" | "external-ok";
174
+ secret_bearing_context?: boolean;
175
+ memory_write_permission?: boolean;
176
+ }
177
+ interface RuntimePolicyDecision {
178
+ action: "allow" | "block";
179
+ reason: string;
180
+ policyId: string;
181
+ amplificationReasons: string[];
182
+ remediationSuggestion: string;
183
+ }
184
+ interface ThreatModel {
185
+ timestamp: string;
186
+ surface: Record<string, boolean>;
187
+ summary: string;
188
+ owasp_asi?: string[];
189
+ layer_summary?: Array<Record<string, unknown>>;
190
+ protocol_surfaces?: string[];
191
+ }
192
+ interface GuardScannerInstance {
193
+ verbose: boolean;
194
+ strict: boolean;
195
+ summaryOnly: boolean;
196
+ quiet: boolean;
197
+ checkDeps: boolean;
198
+ soulLock: boolean;
199
+ thresholds: ThresholdBand;
200
+ findings: SkillFindingResult[];
201
+ stats: ScanStats;
202
+ scanText(text: string): TextScanResult;
203
+ scanDirectory(dir: string): SkillFindingResult[];
204
+ scanTarget(targetPath: string): ScanReport;
205
+ toJSON(): ScanReport;
206
+ toSARIF(scanDir: string): SarifReport;
207
+ toHTML(): string;
208
+ generateThreatModel(findings: Finding[]): ThreatModel;
209
+ }
210
+ interface GuardScannerConstructor {
211
+ new (options?: ScannerOptions): GuardScannerInstance;
212
+ }
213
+ type ScanResult = SkillFindingResult;
214
+
215
+ export type { CapabilityMetrics, CustomRule, EvidenceSpan, Finding, FindingEvidence, GuardMode, GuardScannerConstructor, GuardScannerInstance, McpRequest, PluginConfig, QualityTargets, Recommendation, RuntimeAction, RuntimeCheckStats, RuntimeDecision, RuntimePolicyContract, RuntimePolicyDecision, SarifReport, ScanReport, ScanResult, ScanStats, ScannerOptions, Severity, SkillFindingResult, TextScanResult, ThreatModel, ThresholdBand };
@@ -0,0 +1,215 @@
1
+ type Severity = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
2
+ type GuardMode = "monitor" | "enforce" | "strict";
3
+ type RuntimeAction = "blocked" | "warned";
4
+ interface EvidenceSpan {
5
+ file?: string;
6
+ start_line: number;
7
+ end_line: number;
8
+ }
9
+ interface FindingEvidence {
10
+ file?: string;
11
+ line?: number | null;
12
+ sample?: string;
13
+ match_count?: number;
14
+ tool_name?: string;
15
+ params_preview?: string;
16
+ layer?: number;
17
+ layer_name?: string;
18
+ owasp_asi?: string[];
19
+ protocol_surface?: string[];
20
+ }
21
+ interface Finding {
22
+ schema_version?: string;
23
+ source?: "static" | "runtime";
24
+ id: string;
25
+ rule_id?: string;
26
+ cat?: string;
27
+ category: string;
28
+ severity: Severity;
29
+ desc?: string;
30
+ description: string;
31
+ file?: string;
32
+ line?: number | null;
33
+ matchCount?: number;
34
+ sample?: string;
35
+ rationale: string;
36
+ preconditions: string;
37
+ remediation_hint: string;
38
+ false_positive_scenarios: string[];
39
+ validation_state: string;
40
+ validation_status: string;
41
+ confidence: number;
42
+ attack_chain_id: string | null;
43
+ evidence: FindingEvidence;
44
+ evidence_spans: EvidenceSpan[];
45
+ layer?: number;
46
+ layer_name?: string;
47
+ owasp_asi?: string[];
48
+ protocol_surface?: string[];
49
+ action?: RuntimeAction;
50
+ }
51
+ interface SkillFindingResult {
52
+ skill: string;
53
+ risk: number;
54
+ verdict: string;
55
+ findings: Finding[];
56
+ }
57
+ interface ThresholdBand {
58
+ suspicious: number;
59
+ malicious: number;
60
+ }
61
+ interface ScanStats {
62
+ scanned: number;
63
+ clean: number;
64
+ low: number;
65
+ suspicious: number;
66
+ malicious: number;
67
+ }
68
+ interface Recommendation {
69
+ skill: string;
70
+ actions: string[];
71
+ }
72
+ interface ScanReport {
73
+ schema_version: string;
74
+ timestamp: string;
75
+ scanner: string;
76
+ finding_schema_version: string;
77
+ mode: "normal" | "strict";
78
+ compliance_mode?: "owasp-asi" | null;
79
+ stats: ScanStats;
80
+ thresholds: ThresholdBand;
81
+ findings: SkillFindingResult[];
82
+ recommendations: Recommendation[];
83
+ layer_summary?: Array<Record<string, unknown>>;
84
+ owasp_asi_coverage?: Array<Record<string, unknown>>;
85
+ threat_model?: Record<string, unknown>;
86
+ iocVersion: string;
87
+ }
88
+ interface TextScanResult {
89
+ safe: boolean;
90
+ risk: number;
91
+ detections: Finding[];
92
+ }
93
+ interface ScannerOptions {
94
+ verbose?: boolean;
95
+ selfExclude?: boolean;
96
+ strict?: boolean;
97
+ summaryOnly?: boolean;
98
+ quiet?: boolean;
99
+ checkDeps?: boolean;
100
+ soulLock?: boolean;
101
+ plugins?: string[];
102
+ rulesFile?: string;
103
+ compliance?: "owasp-asi";
104
+ }
105
+ interface CustomRule {
106
+ id: string;
107
+ cat: string;
108
+ regex: RegExp;
109
+ severity: Severity;
110
+ desc: string;
111
+ codeOnly?: boolean;
112
+ docOnly?: boolean;
113
+ all?: boolean;
114
+ soulLock?: boolean;
115
+ }
116
+ interface PluginConfig {
117
+ mode?: GuardMode;
118
+ auditLog?: boolean;
119
+ customRules?: string;
120
+ }
121
+ interface RuntimeDecision {
122
+ blocked: boolean;
123
+ blockReason: string | null;
124
+ detections: Finding[];
125
+ mode: GuardMode;
126
+ toolName?: string;
127
+ matchedPolicyId?: string | null;
128
+ policyRationale?: string | null;
129
+ riskAmplificationReasons?: string[];
130
+ remediationSuggestion?: string | null;
131
+ policyDecision?: RuntimePolicyDecision | null;
132
+ }
133
+ interface McpRequest {
134
+ method: string;
135
+ params?: Record<string, unknown>;
136
+ id?: string | number | null;
137
+ }
138
+ interface SarifReport {
139
+ version: string;
140
+ $schema?: string;
141
+ runs: Array<Record<string, unknown>>;
142
+ }
143
+ interface CapabilityMetrics {
144
+ static_pattern_count: number;
145
+ runtime_check_count?: number;
146
+ threat_category_count: number;
147
+ runtime_layer_count?: number;
148
+ runtime_layers?: number;
149
+ benchmark_corpus_version?: string;
150
+ explainability_completeness_rate?: number;
151
+ runtime_check_latency_budget_ms?: number;
152
+ quality_targets?: QualityTargets;
153
+ [key: string]: unknown;
154
+ }
155
+ interface RuntimeCheckStats {
156
+ total: number;
157
+ byLayer: Record<number, number>;
158
+ bySeverity: Partial<Record<Severity, number>>;
159
+ }
160
+ interface QualityTargets {
161
+ precision_min: number;
162
+ recall_min: number;
163
+ false_positive_rate_max: number;
164
+ false_negative_rate_max: number;
165
+ explainability_completeness_rate_min: number;
166
+ runtime_check_latency_budget_ms: number;
167
+ false_positive_budget_by_category: Record<string, number>;
168
+ }
169
+ interface RuntimePolicyContract {
170
+ id?: string;
171
+ allowed_tools?: string[];
172
+ blocked_tools?: string[];
173
+ max_network_scope?: "none" | "internal-only" | "external-ok";
174
+ secret_bearing_context?: boolean;
175
+ memory_write_permission?: boolean;
176
+ }
177
+ interface RuntimePolicyDecision {
178
+ action: "allow" | "block";
179
+ reason: string;
180
+ policyId: string;
181
+ amplificationReasons: string[];
182
+ remediationSuggestion: string;
183
+ }
184
+ interface ThreatModel {
185
+ timestamp: string;
186
+ surface: Record<string, boolean>;
187
+ summary: string;
188
+ owasp_asi?: string[];
189
+ layer_summary?: Array<Record<string, unknown>>;
190
+ protocol_surfaces?: string[];
191
+ }
192
+ interface GuardScannerInstance {
193
+ verbose: boolean;
194
+ strict: boolean;
195
+ summaryOnly: boolean;
196
+ quiet: boolean;
197
+ checkDeps: boolean;
198
+ soulLock: boolean;
199
+ thresholds: ThresholdBand;
200
+ findings: SkillFindingResult[];
201
+ stats: ScanStats;
202
+ scanText(text: string): TextScanResult;
203
+ scanDirectory(dir: string): SkillFindingResult[];
204
+ scanTarget(targetPath: string): ScanReport;
205
+ toJSON(): ScanReport;
206
+ toSARIF(scanDir: string): SarifReport;
207
+ toHTML(): string;
208
+ generateThreatModel(findings: Finding[]): ThreatModel;
209
+ }
210
+ interface GuardScannerConstructor {
211
+ new (options?: ScannerOptions): GuardScannerInstance;
212
+ }
213
+ type ScanResult = SkillFindingResult;
214
+
215
+ export type { CapabilityMetrics, CustomRule, EvidenceSpan, Finding, FindingEvidence, GuardMode, GuardScannerConstructor, GuardScannerInstance, McpRequest, PluginConfig, QualityTargets, Recommendation, RuntimeAction, RuntimeCheckStats, RuntimeDecision, RuntimePolicyContract, RuntimePolicyDecision, SarifReport, ScanReport, ScanResult, ScanStats, ScannerOptions, Severity, SkillFindingResult, TextScanResult, ThreatModel, ThresholdBand };
package/dist/types.mjs ADDED
@@ -0,0 +1 @@
1
+ import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
@@ -0,0 +1,182 @@
1
+ # Evidence-Driven Metrics
2
+
3
+ guard-scanner uses a **single source of truth (SSoT)** architecture for all public metrics. This ensures that numbers in README, documentation, and tests are always in sync with the implementation.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ ┌─────────────────────────────────────┐
9
+ │ Source Code (patterns.js, etc.) │
10
+ └──────────────┬──────────────────────┘
11
+
12
+
13
+ ┌─────────────────────────────────────┐
14
+ │ generate-capabilities.js │
15
+ │ Generates: docs/spec/capabilities.json
16
+ └──────────────┬──────────────────────┘
17
+
18
+
19
+ ┌─────────────────────────────────────┐
20
+ │ capabilities.json (SSoT) │
21
+ │ - static_pattern_count: 352 │
22
+ │ - threat_category_count: 32 │
23
+ │ - runtime_check_count: 26 │
24
+ │ - mcp_tools: [...] │
25
+ └──────┬───────────────────────┬──────┘
26
+ │ │
27
+ ▼ ▼
28
+ ┌──────────────┐ ┌────────────────┐
29
+ │ generate- │ │ verify- │
30
+ │ readme- │ │ capabilities.js│
31
+ │ metrics.js │ │ (CI check) │
32
+ └──────┬───────┘ └────────────────┘
33
+
34
+
35
+ ┌─────────────────────────────────────┐
36
+ │ README.md │
37
+ │ (Auto-updated metrics) │
38
+ └─────────────────────────────────────┘
39
+ ```
40
+
41
+ ## Scripts
42
+
43
+ ### 1. `generate-capabilities.js`
44
+
45
+ **Purpose:** Generate `docs/spec/capabilities.json` from source code.
46
+
47
+ **Runs:**
48
+ - Counts patterns from `src/patterns.js`
49
+ - Counts runtime checks from `src/runtime-guard.js`
50
+ - Lists MCP tools from `src/mcp-server.js`
51
+ - Reads versions from `package.json` and `openclaw.plugin.json`
52
+
53
+ **Usage:**
54
+ ```bash
55
+ node scripts/generate-capabilities.js
56
+ ```
57
+
58
+ **Output:** `docs/spec/capabilities.json`
59
+
60
+ ### 2. `generate-readme-metrics.js`
61
+
62
+ **Purpose:** Inject metrics from `capabilities.json` into `README.md`.
63
+
64
+ **Updates:**
65
+ - Header metrics line (categories, patterns, checks)
66
+ - Dependency badge
67
+ - Capability table entries
68
+ - MCP tool descriptions
69
+
70
+ **Usage:**
71
+ ```bash
72
+ # Update README
73
+ node scripts/generate-readme-metrics.js
74
+
75
+ # CI mode: fail if drift detected
76
+ node scripts/generate-readme-metrics.js --check
77
+ ```
78
+
79
+ ### 3. `generate-readme-stats.js`
80
+
81
+ **Purpose:** Inject test counts from `npm test` output into README.
82
+
83
+ **Updates:**
84
+ - Test badge: `tests-336%20passed`
85
+ - Test results block
86
+
87
+ **Usage:**
88
+ ```bash
89
+ # Update README
90
+ node scripts/generate-readme-stats.js
91
+
92
+ # CI mode: fail if drift detected
93
+ node scripts/generate-readme-stats.js --check
94
+ ```
95
+
96
+ ### 4. `verify-capabilities.js`
97
+
98
+ **Purpose:** Verify all documentation matches `capabilities.json`.
99
+
100
+ **Checks:**
101
+ - README.md metrics
102
+ - README_ja.md metrics
103
+ - SKILL.md metrics
104
+ - package.json version
105
+ - openclaw.plugin.json version
106
+ - Test file count
107
+
108
+ **Usage:**
109
+ ```bash
110
+ node scripts/verify-capabilities.js
111
+ # Exits 1 if any drift detected
112
+ ```
113
+
114
+ ## CI Integration
115
+
116
+ The CI workflow enforces zero-tolerance for drift:
117
+
118
+ ```yaml
119
+ # .github/workflows/ci.yml
120
+ - name: Generate capabilities manifest
121
+ run: node scripts/generate-capabilities.js
122
+
123
+ - name: Check README metrics drift
124
+ run: |
125
+ node scripts/generate-readme-metrics.js --check
126
+ node scripts/generate-readme-stats.js --check
127
+
128
+ - name: Verify all capability claims
129
+ run: node scripts/verify-capabilities.js
130
+ ```
131
+
132
+ ## Local Development
133
+
134
+ **Sync all README metrics:**
135
+ ```bash
136
+ npm run sync:readme
137
+ ```
138
+
139
+ This runs:
140
+ 1. `generate-capabilities.js` (update SSoT)
141
+ 2. `generate-readme-metrics.js` (update metrics)
142
+ 3. `generate-readme-stats.js` (update test counts)
143
+
144
+ ## Adding New Metrics
145
+
146
+ 1. **Add to source code:** Update `patterns.js`, `runtime-guard.js`, etc.
147
+ 2. **Update generator:** Edit `generate-capabilities.js` to extract new metric
148
+ 3. **Update README generator:** Edit `generate-readme-metrics.js` to inject into README
149
+ 4. **Update verifier:** Edit `verify-capabilities.js` to check for drift
150
+ 5. **Run sync:** `npm run sync:readme`
151
+ 6. **Commit changes:** Include updated `capabilities.json` and `README.md`
152
+
153
+ ## Philosophy
154
+
155
+ **Why evidence-driven?**
156
+
157
+ - **Trust:** Users can verify claims match implementation
158
+ - **Marketing-first avoidance:** Numbers come from code, not marketing
159
+ - **Drift prevention:** CI blocks PRs with mismatched numbers
160
+ - **Single source of truth:** One canonical source (`capabilities.json`)
161
+ - **Audit trail:** All changes go through generators
162
+
163
+ **Zero tolerance for hardcoded numbers in public docs.**
164
+
165
+ ## MCP Integration
166
+
167
+ The `get_stats` MCP tool reads from `capabilities.json`:
168
+
169
+ ```javascript
170
+ function handleGetStats() {
171
+ const runtimeStats = getCheckStats();
172
+ return successResult(
173
+ `🛡️ guard-scanner v${VERSION}\n\n` +
174
+ `Static Analysis:\n` +
175
+ ` • ${STATIC_SUMMARY}\n` + // from capabilities.json
176
+ ` • ${runtimeStats.total} checks across ${Object.keys(runtimeStats.byLayer).length} layers\n` +
177
+ // ...
178
+ );
179
+ }
180
+ ```
181
+
182
+ This ensures MCP clients always get accurate, up-to-date metrics.
package/docs/banner.png CHANGED
Binary file