@rmyndharis/aimhooman 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/rules/aimhooman.md +53 -0
- package/.claude-plugin/marketplace.json +23 -0
- package/.claude-plugin/plugin.json +9 -0
- package/.clinerules/aimhooman.md +53 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/aimhooman.mdc +59 -0
- package/.gemini/settings.json +7 -0
- package/.github/copilot-instructions.md +53 -0
- package/.github/hooks/aimhooman.json +22 -0
- package/.kiro/steering/aimhooman.md +53 -0
- package/.windsurf/rules/aimhooman.md +57 -0
- package/AGENTS.md +53 -0
- package/CHANGELOG.md +153 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +144 -0
- package/GEMINI.md +53 -0
- package/LICENSE +21 -0
- package/README.md +472 -0
- package/SECURITY.md +74 -0
- package/bin/aimhooman.mjs +1589 -0
- package/docs/design/agent-portability.md +73 -0
- package/docs/design/frictionless-enforcement.md +135 -0
- package/docs/hosts.json +164 -0
- package/docs/logo/aimhooman-logo.png +0 -0
- package/docs/logo/aimhooman.png +0 -0
- package/hooks/hooks.json +29 -0
- package/package.json +77 -0
- package/rules/attribution.json +142 -0
- package/rules/markers.json +88 -0
- package/rules/paths.json +407 -0
- package/rules/secrets.json +90 -0
- package/schemas/overrides.schema.json +134 -0
- package/schemas/project-policy.schema.json +12 -0
- package/schemas/rule-pack.schema.json +126 -0
- package/schemas/scan-report.schema.json +165 -0
- package/skills/aimhooman/SKILL.md +65 -0
- package/src/args.mjs +72 -0
- package/src/atomic-write.mjs +285 -0
- package/src/codex-manifest.mjs +65 -0
- package/src/exclude.mjs +125 -0
- package/src/git-environment.mjs +5 -0
- package/src/git-path.mjs +5 -0
- package/src/githooks.mjs +813 -0
- package/src/gitx.mjs +721 -0
- package/src/history-scan.mjs +295 -0
- package/src/hook.mjs +2602 -0
- package/src/policy-resolver.mjs +200 -0
- package/src/report.mjs +63 -0
- package/src/rules.mjs +509 -0
- package/src/ruleset-text.mjs +29 -0
- package/src/scan-session.mjs +152 -0
- package/src/scan-target.mjs +697 -0
- package/src/scan.mjs +325 -0
- package/src/state.mjs +360 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rmyndharis/aimhooman/schemas/overrides.schema.json",
|
|
4
|
+
"title": "aimhooman local overrides",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"schema_version": { "const": 1 },
|
|
8
|
+
"allow": { "$ref": "#/$defs/allowEntries" },
|
|
9
|
+
"deny": { "$ref": "#/$defs/denyEntries" }
|
|
10
|
+
},
|
|
11
|
+
"$defs": {
|
|
12
|
+
"ordinaryAllow": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["target"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"target": { "type": "string", "minLength": 1 },
|
|
17
|
+
"scope": { "enum": ["path", "rule", "secret-path"] },
|
|
18
|
+
"reason": { "type": "string" },
|
|
19
|
+
"actor": { "type": "string" },
|
|
20
|
+
"at": { "type": "string", "format": "date-time" }
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"reviewedAllow": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["target", "scope", "head", "transition", "newObjectId", "newMode"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"target": { "type": "string", "minLength": 1 },
|
|
29
|
+
"scope": { "enum": ["reviewed-instruction", "reviewed-policy-file"] },
|
|
30
|
+
"reason": { "type": "string" },
|
|
31
|
+
"actor": { "type": "string" },
|
|
32
|
+
"at": { "type": "string", "format": "date-time" },
|
|
33
|
+
"head": { "$ref": "#/$defs/objectId" },
|
|
34
|
+
"transition": {
|
|
35
|
+
"oneOf": [
|
|
36
|
+
{ "const": "staged" },
|
|
37
|
+
{ "$ref": "#/$defs/objectId" }
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"newObjectId": { "$ref": "#/$defs/nullableObjectId" },
|
|
41
|
+
"newMode": { "$ref": "#/$defs/nullableBlobMode" }
|
|
42
|
+
},
|
|
43
|
+
"allOf": [
|
|
44
|
+
{
|
|
45
|
+
"if": { "properties": { "newObjectId": { "type": "null" } } },
|
|
46
|
+
"then": { "properties": { "newMode": { "type": "null" } } },
|
|
47
|
+
"else": { "properties": { "newMode": { "$ref": "#/$defs/blobMode" } } }
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"additionalProperties": false
|
|
51
|
+
},
|
|
52
|
+
"migrationAllow": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"required": ["target", "scope", "head", "transition", "oldObjectId", "newObjectId", "newMode"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"target": { "const": ".aimhooman.json" },
|
|
57
|
+
"scope": { "const": "policy-migration" },
|
|
58
|
+
"reason": { "type": "string" },
|
|
59
|
+
"actor": { "type": "string" },
|
|
60
|
+
"at": { "type": "string", "format": "date-time" },
|
|
61
|
+
"head": { "$ref": "#/$defs/objectId" },
|
|
62
|
+
"transition": {
|
|
63
|
+
"anyOf": [
|
|
64
|
+
{ "const": "staged" },
|
|
65
|
+
{ "$ref": "#/$defs/objectId" }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"oldObjectId": { "$ref": "#/$defs/objectId" },
|
|
69
|
+
"newObjectId": {
|
|
70
|
+
"anyOf": [
|
|
71
|
+
{ "$ref": "#/$defs/objectId" },
|
|
72
|
+
{ "type": "null" }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"newMode": { "$ref": "#/$defs/nullableBlobMode" }
|
|
76
|
+
},
|
|
77
|
+
"allOf": [
|
|
78
|
+
{
|
|
79
|
+
"if": { "properties": { "newObjectId": { "type": "null" } } },
|
|
80
|
+
"then": { "properties": { "newMode": { "type": "null" } } },
|
|
81
|
+
"else": { "properties": { "newMode": { "$ref": "#/$defs/blobMode" } } }
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"additionalProperties": false
|
|
85
|
+
},
|
|
86
|
+
"allowEntries": {
|
|
87
|
+
"type": "array",
|
|
88
|
+
"items": {
|
|
89
|
+
"oneOf": [
|
|
90
|
+
{ "$ref": "#/$defs/ordinaryAllow" },
|
|
91
|
+
{ "$ref": "#/$defs/reviewedAllow" },
|
|
92
|
+
{ "$ref": "#/$defs/migrationAllow" }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"denyEntries": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"items": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"required": ["target"],
|
|
101
|
+
"properties": {
|
|
102
|
+
"target": { "type": "string", "minLength": 1 },
|
|
103
|
+
"scope": { "enum": ["path", "rule"] },
|
|
104
|
+
"reason": { "type": "string" },
|
|
105
|
+
"actor": { "type": "string" },
|
|
106
|
+
"at": { "type": "string", "format": "date-time" }
|
|
107
|
+
},
|
|
108
|
+
"additionalProperties": false
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"objectId": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"$comment": "Accepts mixed case because overrides.json is user-authored; the loader lowercases head/transition/oldObjectId/newObjectId on read. scan-report.schema.json uses lowercase-only because it validates Git's normalized output.",
|
|
114
|
+
"pattern": "^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$"
|
|
115
|
+
},
|
|
116
|
+
"nullableObjectId": {
|
|
117
|
+
"anyOf": [
|
|
118
|
+
{ "$ref": "#/$defs/objectId" },
|
|
119
|
+
{ "type": "null" }
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"blobMode": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"enum": ["100644", "100755"]
|
|
125
|
+
},
|
|
126
|
+
"nullableBlobMode": {
|
|
127
|
+
"anyOf": [
|
|
128
|
+
{ "$ref": "#/$defs/blobMode" },
|
|
129
|
+
{ "type": "null" }
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"additionalProperties": false
|
|
134
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rmyndharis/aimhooman/schemas/project-policy.schema.json",
|
|
4
|
+
"title": "aimhooman project policy",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schema_version", "profile"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": { "const": 1 },
|
|
9
|
+
"profile": { "enum": ["clean", "strict", "compliance"] }
|
|
10
|
+
},
|
|
11
|
+
"additionalProperties": false
|
|
12
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rmyndharis/aimhooman/schemas/rule-pack.schema.json",
|
|
4
|
+
"title": "aimhooman local rule pack",
|
|
5
|
+
"description": "Structural schema for local packs. The loader also applies the documented content-expression and path-glob work limits.",
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"required": ["id", "provider", "category", "kind", "match", "actions", "reason"],
|
|
10
|
+
"properties": {
|
|
11
|
+
"id": { "type": "string", "minLength": 1 },
|
|
12
|
+
"version": { "type": "integer", "minimum": 1 },
|
|
13
|
+
"provider": { "type": "string", "minLength": 1 },
|
|
14
|
+
"category": { "type": "string", "minLength": 1 },
|
|
15
|
+
"confidence": { "type": "string", "minLength": 1 },
|
|
16
|
+
"kind": { "enum": ["path", "message", "code"] },
|
|
17
|
+
"match": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"paths": { "$ref": "#/$defs/nonEmptyStrings" },
|
|
21
|
+
"except": { "$ref": "#/$defs/strings" },
|
|
22
|
+
"content": { "$ref": "#/$defs/nonEmptyStrings" },
|
|
23
|
+
"path_case": { "enum": ["sensitive", "insensitive"] }
|
|
24
|
+
},
|
|
25
|
+
"additionalProperties": false
|
|
26
|
+
},
|
|
27
|
+
"actions": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"properties": {
|
|
30
|
+
"clean": { "$ref": "#/$defs/action" },
|
|
31
|
+
"strict": { "$ref": "#/$defs/action" },
|
|
32
|
+
"compliance": { "$ref": "#/$defs/action" }
|
|
33
|
+
},
|
|
34
|
+
"minProperties": 1,
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
},
|
|
37
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
38
|
+
"remediation": { "$ref": "#/$defs/strings" },
|
|
39
|
+
"references": { "$ref": "#/$defs/strings" }
|
|
40
|
+
},
|
|
41
|
+
"allOf": [
|
|
42
|
+
{
|
|
43
|
+
"if": { "properties": { "kind": { "const": "path" } }, "required": ["kind"] },
|
|
44
|
+
"then": {
|
|
45
|
+
"properties": {
|
|
46
|
+
"match": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"paths": true,
|
|
50
|
+
"content": true,
|
|
51
|
+
"path_case": true
|
|
52
|
+
},
|
|
53
|
+
"required": ["paths"],
|
|
54
|
+
"not": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": { "content": true },
|
|
57
|
+
"required": ["content"]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"if": { "properties": { "kind": { "const": "message" } }, "required": ["kind"] },
|
|
65
|
+
"then": {
|
|
66
|
+
"properties": {
|
|
67
|
+
"match": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"content": true,
|
|
71
|
+
"paths": true,
|
|
72
|
+
"except": true,
|
|
73
|
+
"path_case": true
|
|
74
|
+
},
|
|
75
|
+
"required": ["content"],
|
|
76
|
+
"not": {
|
|
77
|
+
"anyOf": [
|
|
78
|
+
{
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": { "paths": true },
|
|
81
|
+
"required": ["paths"]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": { "except": true },
|
|
86
|
+
"required": ["except"]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": { "path_case": true },
|
|
91
|
+
"required": ["path_case"]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"if": { "properties": { "kind": { "const": "code" } }, "required": ["kind"] },
|
|
101
|
+
"then": {
|
|
102
|
+
"properties": {
|
|
103
|
+
"match": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": { "content": true },
|
|
106
|
+
"required": ["content"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"additionalProperties": false
|
|
113
|
+
},
|
|
114
|
+
"$defs": {
|
|
115
|
+
"action": { "enum": ["allow", "review", "block"] },
|
|
116
|
+
"strings": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": { "type": "string", "minLength": 1 }
|
|
119
|
+
},
|
|
120
|
+
"nonEmptyStrings": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"minItems": 1,
|
|
123
|
+
"items": { "type": "string", "minLength": 1 }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rmyndharis/aimhooman/schemas/scan-report.schema.json",
|
|
4
|
+
"title": "aimhooman scan report",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"schema_version",
|
|
8
|
+
"tool_version",
|
|
9
|
+
"target",
|
|
10
|
+
"profile",
|
|
11
|
+
"policy_source",
|
|
12
|
+
"policy_object_id",
|
|
13
|
+
"complete",
|
|
14
|
+
"stats",
|
|
15
|
+
"message_scanned",
|
|
16
|
+
"findings"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"schema_version": { "const": 1 },
|
|
20
|
+
"tool_version": { "type": "string", "minLength": 1 },
|
|
21
|
+
"target": { "type": "string", "minLength": 1 },
|
|
22
|
+
"profile": { "enum": ["clean", "strict", "compliance"] },
|
|
23
|
+
"policy_source": { "type": "string", "minLength": 1 },
|
|
24
|
+
"policy_object_id": { "$ref": "#/$defs/nullableObjectId" },
|
|
25
|
+
"policy_enforced_object_ids": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": { "$ref": "#/$defs/objectId" },
|
|
28
|
+
"uniqueItems": true
|
|
29
|
+
},
|
|
30
|
+
"complete": { "type": "boolean" },
|
|
31
|
+
"stats": { "$ref": "#/$defs/stats" },
|
|
32
|
+
"message_scanned": { "type": "boolean" },
|
|
33
|
+
"commit": { "$ref": "#/$defs/objectId" },
|
|
34
|
+
"range": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"required": ["base", "scan_base", "head", "commits_scanned"],
|
|
37
|
+
"properties": {
|
|
38
|
+
"base": { "$ref": "#/$defs/objectId" },
|
|
39
|
+
"scan_base": { "$ref": "#/$defs/objectId" },
|
|
40
|
+
"head": { "$ref": "#/$defs/objectId" },
|
|
41
|
+
"commits_scanned": { "type": "integer", "minimum": 0 }
|
|
42
|
+
},
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
},
|
|
45
|
+
"findings": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "$ref": "#/$defs/finding" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"$defs": {
|
|
52
|
+
"objectId": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$"
|
|
55
|
+
},
|
|
56
|
+
"nullableObjectId": {
|
|
57
|
+
"anyOf": [
|
|
58
|
+
{ "$ref": "#/$defs/objectId" },
|
|
59
|
+
{ "type": "null" }
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"stats": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"required": [
|
|
65
|
+
"entries",
|
|
66
|
+
"blob_files",
|
|
67
|
+
"objects_read",
|
|
68
|
+
"files_scanned",
|
|
69
|
+
"bytes_scanned",
|
|
70
|
+
"findings_total",
|
|
71
|
+
"findings_returned",
|
|
72
|
+
"skipped"
|
|
73
|
+
],
|
|
74
|
+
"properties": {
|
|
75
|
+
"entries": { "type": "integer", "minimum": 0 },
|
|
76
|
+
"blob_files": { "type": "integer", "minimum": 0 },
|
|
77
|
+
"objects_read": { "type": "integer", "minimum": 0 },
|
|
78
|
+
"files_scanned": { "type": "integer", "minimum": 0 },
|
|
79
|
+
"bytes_scanned": { "type": "integer", "minimum": 0 },
|
|
80
|
+
"findings_total": { "type": "integer", "minimum": 0 },
|
|
81
|
+
"findings_returned": { "type": "integer", "minimum": 0 },
|
|
82
|
+
"skipped": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"additionalProperties": { "type": "integer", "minimum": 1 }
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"additionalProperties": false
|
|
88
|
+
},
|
|
89
|
+
"matchedRule": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"required": [
|
|
92
|
+
"ruleId", "ruleVersion", "kind", "category", "provider", "confidence",
|
|
93
|
+
"decision", "reason", "remediation", "source"
|
|
94
|
+
],
|
|
95
|
+
"properties": {
|
|
96
|
+
"ruleId": { "type": "string", "minLength": 1 },
|
|
97
|
+
"ruleVersion": { "type": ["integer", "null"], "minimum": 1 },
|
|
98
|
+
"kind": { "enum": ["path", "message", "code"] },
|
|
99
|
+
"category": { "type": "string", "minLength": 1 },
|
|
100
|
+
"provider": { "type": "string", "minLength": 1 },
|
|
101
|
+
"confidence": { "type": ["string", "null"] },
|
|
102
|
+
"decision": { "enum": ["allow", "review", "block"] },
|
|
103
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
104
|
+
"remediation": { "type": "array", "items": { "type": "string" } },
|
|
105
|
+
"source": { "enum": ["builtin", "local"] }
|
|
106
|
+
},
|
|
107
|
+
"additionalProperties": false
|
|
108
|
+
},
|
|
109
|
+
"finding": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"required": [
|
|
112
|
+
"ruleId", "ruleVersion", "matchedRuleIds", "matchedRules", "kind", "category",
|
|
113
|
+
"provider", "confidence", "decision", "reason", "remediation", "source",
|
|
114
|
+
"scanProfile", "policySource", "policyObjectId"
|
|
115
|
+
],
|
|
116
|
+
"properties": {
|
|
117
|
+
"ruleId": { "type": "string", "minLength": 1 },
|
|
118
|
+
"ruleVersion": { "type": ["integer", "null"], "minimum": 1 },
|
|
119
|
+
"matchedRuleIds": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"minItems": 1,
|
|
122
|
+
"items": { "type": "string", "minLength": 1 },
|
|
123
|
+
"uniqueItems": true
|
|
124
|
+
},
|
|
125
|
+
"matchedRules": {
|
|
126
|
+
"type": "array",
|
|
127
|
+
"minItems": 1,
|
|
128
|
+
"items": { "$ref": "#/$defs/matchedRule" }
|
|
129
|
+
},
|
|
130
|
+
"kind": { "enum": ["path", "message", "code"] },
|
|
131
|
+
"category": { "type": "string", "minLength": 1 },
|
|
132
|
+
"provider": { "type": "string", "minLength": 1 },
|
|
133
|
+
"confidence": { "type": ["string", "null"] },
|
|
134
|
+
"decision": { "enum": ["block", "review"] },
|
|
135
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
136
|
+
"remediation": { "type": "array", "items": { "type": "string" } },
|
|
137
|
+
"source": { "enum": ["builtin", "local"] },
|
|
138
|
+
"path": { "type": "string" },
|
|
139
|
+
"line": { "type": "integer", "minimum": 1 },
|
|
140
|
+
"text": { "type": "string" },
|
|
141
|
+
"autofix": { "const": "remove-whole-line" },
|
|
142
|
+
"commit": { "$ref": "#/$defs/objectId" },
|
|
143
|
+
"objectId": { "$ref": "#/$defs/objectId" },
|
|
144
|
+
"parents": {
|
|
145
|
+
"type": "array",
|
|
146
|
+
"items": { "$ref": "#/$defs/objectId" },
|
|
147
|
+
"uniqueItems": true
|
|
148
|
+
},
|
|
149
|
+
"status": { "type": "string" },
|
|
150
|
+
"statuses": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
|
|
151
|
+
"sourcePath": { "type": "string" },
|
|
152
|
+
"sourcePaths": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
|
|
153
|
+
"scanProfile": { "enum": ["clean", "strict", "compliance"] },
|
|
154
|
+
"policySource": { "type": "string", "minLength": 1 },
|
|
155
|
+
"policyObjectId": { "$ref": "#/$defs/nullableObjectId" },
|
|
156
|
+
"policyEnforcedObjectIds": {
|
|
157
|
+
"type": "array",
|
|
158
|
+
"items": { "$ref": "#/$defs/objectId" },
|
|
159
|
+
"uniqueItems": true
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"additionalProperties": false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aimhooman
|
|
3
|
+
description: >
|
|
4
|
+
Keep AI tooling artifacts out of Git. Use whenever staging, committing, or
|
|
5
|
+
pushing changes: block AI session/state files (.claude/session.json,
|
|
6
|
+
.codex/history, .copilot, .cursor/session, .aider.*, .specstory, .agent),
|
|
7
|
+
secrets (.env, private keys, .aws/credentials), and unwanted AI attribution in
|
|
8
|
+
commit messages (Co-authored-by an AI, "Generated with AI"). Also use when the user says
|
|
9
|
+
"aimhooman", "ship it like a hooman", or asks to clean AI residue before a commit.
|
|
10
|
+
license: MIT
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# aimhooman
|
|
14
|
+
|
|
15
|
+
<!-- aimhooman:ruleset-start -->
|
|
16
|
+
This repository uses aimhooman to keep AI tooling artifacts out of Git history.
|
|
17
|
+
Its policy:
|
|
18
|
+
|
|
19
|
+
- Never stage or commit local AI session/state files. Examples include
|
|
20
|
+
`.claude.json`, `.claude/.credentials.json`, `.claude/session*.json`,
|
|
21
|
+
`.claude/history*`, `.claude/todos/*`, `.claude/shell-snapshots/*`,
|
|
22
|
+
`.claude/statsig/*`, `.claude/projects/*`, `.claude/logs/*`,
|
|
23
|
+
`.codex/sessions/*`, `.codex/history*`, `.codex/log/*`, `.codex/logs/*`,
|
|
24
|
+
`.copilot/*`, `.cursor/session*`, `.cursor/chats/*`, `.cursor/logs/*`,
|
|
25
|
+
`.aider.*`, `.specstory/*`, `.continue/sessions/*`, `.playwright-mcp/*`,
|
|
26
|
+
`.remember/*`, `.superpowers/*`, and `.agent/*`. The examples are not
|
|
27
|
+
exhaustive; the packaged `rules/paths.json` is the detection source of truth.
|
|
28
|
+
If one is staged, unstage it and keep it out of Git instead.
|
|
29
|
+
- Never commit secrets: a real `.env` (not `.env.example`), private keys
|
|
30
|
+
(`id_rsa` and files containing a private-key header), `.aws/credentials`, or
|
|
31
|
+
service-account keys. Public certificates are allowed.
|
|
32
|
+
- Never add AI attribution to commit messages: no `Co-authored-by` trailer naming
|
|
33
|
+
an AI (Claude, Copilot, Codex), no "Generated with/by <AI>" lines, no AI-service
|
|
34
|
+
noreply emails. A commit message reads as if a human wrote it.
|
|
35
|
+
- Write in a human voice, not an AI voice. No `delve`, `leverage`, `utilize`,
|
|
36
|
+
`seamless`, `robust`, `comprehensive`, `underscore`, `foster`, `pivotal`, `myriad`,
|
|
37
|
+
`tapestry`, `landscape`; no "This PR addresses…", "adversarial self-review",
|
|
38
|
+
"live-verified", "thoroughly tested with comprehensive examples", "in today's",
|
|
39
|
+
"let's dive in", "in conclusion"; no bold lead-in on every bullet and no paired
|
|
40
|
+
em-dash asides. Say what broke or what changed, pick the plain short verb, name the
|
|
41
|
+
number, and vary sentence length. If the first line could open any PR on any repo,
|
|
42
|
+
delete it.
|
|
43
|
+
- Treat `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md`
|
|
44
|
+
as review-required: they may be intentional team config, so confirm before
|
|
45
|
+
committing them.
|
|
46
|
+
|
|
47
|
+
Project map:
|
|
48
|
+
|
|
49
|
+
- `src/scan.mjs` and `src/rules.mjs` load rules and make policy decisions.
|
|
50
|
+
- `src/hook.mjs` and `src/githooks.mjs` enforce them in agent and Git hooks.
|
|
51
|
+
- `src/state.mjs` owns local policy state. `bin/aimhooman.mjs` is the CLI.
|
|
52
|
+
- `tests/` mirrors the production paths and includes real temporary Git repos.
|
|
53
|
+
|
|
54
|
+
Use Node.js 22.8 or newer. Before finishing a change, run:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npm run check
|
|
58
|
+
npm test
|
|
59
|
+
npm run test:coverage
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
aimhooman also enforces this at commit time, so a blocked commit means a real
|
|
63
|
+
violation to fix, not a check to bypass. The rule is simple: AI works, hoomans
|
|
64
|
+
ship.
|
|
65
|
+
<!-- aimhooman:ruleset-end -->
|
package/src/args.mjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export class ArgumentError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'ArgumentError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function parseArguments(args, definition = {}) {
|
|
9
|
+
const options = {};
|
|
10
|
+
const positionals = [];
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
const byName = new Map();
|
|
13
|
+
for (const [key, option] of Object.entries(definition.options || {})) {
|
|
14
|
+
for (const name of option.names || []) byName.set(name, { ...option, key });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let positionalOnly = false;
|
|
18
|
+
for (let index = 0; index < args.length; index++) {
|
|
19
|
+
const argument = args[index];
|
|
20
|
+
if (!positionalOnly && argument === '--') {
|
|
21
|
+
positionalOnly = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!positionalOnly && argument.startsWith('-')) {
|
|
25
|
+
const equal = argument.indexOf('=');
|
|
26
|
+
const name = equal > 0 ? argument.slice(0, equal) : argument;
|
|
27
|
+
const inlineValue = equal > 0 ? argument.slice(equal + 1) : undefined;
|
|
28
|
+
const option = byName.get(name);
|
|
29
|
+
if (!option) throw new ArgumentError(`unknown option "${name}"`);
|
|
30
|
+
if (seen.has(option.key) && !option.repeatable) {
|
|
31
|
+
throw new ArgumentError(`option "${name}" may only be used once`);
|
|
32
|
+
}
|
|
33
|
+
seen.add(option.key);
|
|
34
|
+
if (option.type === 'boolean') {
|
|
35
|
+
if (inlineValue !== undefined) throw new ArgumentError(`option "${name}" does not take a value`);
|
|
36
|
+
options[option.key] = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
let value = inlineValue;
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
value = args[++index];
|
|
42
|
+
// Only treat the next token as a missing value when it is itself a
|
|
43
|
+
// registered flag; otherwise accept it (e.g. --reason "- rotated key").
|
|
44
|
+
if (value === undefined || (value.startsWith('-') && byName.has(value))) {
|
|
45
|
+
throw new ArgumentError(`missing value for ${name}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!value && option.nonEmpty !== false) throw new ArgumentError(`missing value for ${name}`);
|
|
49
|
+
if (option.choices && !option.choices.includes(value)) {
|
|
50
|
+
throw new ArgumentError(`invalid value for ${name}: "${value}"`);
|
|
51
|
+
}
|
|
52
|
+
if (option.repeatable) {
|
|
53
|
+
if (!options[option.key]) options[option.key] = [];
|
|
54
|
+
options[option.key].push(value);
|
|
55
|
+
} else {
|
|
56
|
+
options[option.key] = value;
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
positionals.push(argument);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const group of definition.conflicts || []) {
|
|
64
|
+
const active = group.filter((key) => options[key] !== undefined && options[key] !== false);
|
|
65
|
+
if (active.length > 1) throw new ArgumentError(`options conflict: ${active.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
const minimum = definition.minPositionals ?? 0;
|
|
68
|
+
const maximum = definition.maxPositionals ?? Infinity;
|
|
69
|
+
if (positionals.length < minimum) throw new ArgumentError('missing required argument');
|
|
70
|
+
if (positionals.length > maximum) throw new ArgumentError(`unexpected argument "${positionals[maximum]}"`);
|
|
71
|
+
return { options, positionals };
|
|
72
|
+
}
|