@rmyndharis/aimhooman 0.1.2 → 0.1.3
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 +5 -3
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.clinerules/aimhooman.md +5 -3
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/aimhooman.mdc +5 -3
- package/.github/copilot-instructions.md +5 -3
- package/.kiro/steering/aimhooman.md +5 -3
- package/.windsurf/rules/aimhooman.md +5 -3
- package/AGENTS.md +5 -3
- package/CHANGELOG.md +30 -0
- package/GEMINI.md +5 -3
- package/README.md +15 -7
- package/bin/aimhooman.mjs +123 -29
- package/docs/design/frictionless-enforcement.md +28 -0
- package/package.json +1 -1
- package/rules/attribution.json +3 -3
- package/rules/markers.json +31 -5
- package/rules/paths.json +60 -33
- package/rules/secrets.json +2 -1
- package/skills/aimhooman/SKILL.md +5 -3
- package/src/atomic-write.mjs +11 -1
- package/src/gitx.mjs +17 -5
- package/src/history-scan.mjs +4 -0
- package/src/hook.mjs +401 -46
- package/src/scan-session.mjs +9 -3
- package/src/scan-target.mjs +10 -1
package/rules/attribution.json
CHANGED
|
@@ -95,15 +95,15 @@
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
"id": "attribution.generated-with",
|
|
98
|
-
"version":
|
|
98
|
+
"version": 3,
|
|
99
99
|
"provider": "generic",
|
|
100
100
|
"category": "ai-attribution",
|
|
101
101
|
"confidence": "high",
|
|
102
102
|
"kind": "message",
|
|
103
103
|
"match": {
|
|
104
104
|
"content": [
|
|
105
|
-
"(?i)^\\s*generated\\s+(?:with|by)\\s+(?:claude(
|
|
106
|
-
"(?i)^\\s*(?:🤖\\s*)?generated\\s+with\\s+\\[(?:claude(
|
|
105
|
+
"(?i)^\\s*(?:🤖\\s*)?generated\\s+(?:with|by)\\s+(?:claude|(?:github\\s+)?copilot|chatgpt|gpt(?:-[0-9]+(?:\\.[0-9]+)?)?|(?:openai\\s+)?codex|cursor)(?:\\s+[A-Za-z0-9.]+){0,3}[.!]?\\s*(?:🤖\\s*)?$",
|
|
106
|
+
"(?i)^\\s*(?:🤖\\s*)?generated\\s+with\\s+\\[(?:claude|(?:github\\s+)?copilot|chatgpt|gpt(?:-[0-9]+(?:\\.[0-9]+)?)?|(?:openai\\s+)?codex|cursor)(?:\\s+[A-Za-z0-9.]+){0,3}\\]\\([^)]*\\)\\s*(?:🤖\\s*)?$"
|
|
107
107
|
]
|
|
108
108
|
},
|
|
109
109
|
"actions": {
|
package/rules/markers.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"id": "marker.corner-cut",
|
|
4
|
-
"version":
|
|
4
|
+
"version": 3,
|
|
5
5
|
"provider": "generic",
|
|
6
6
|
"category": "ai-marker",
|
|
7
7
|
"confidence": "medium",
|
|
@@ -14,19 +14,32 @@
|
|
|
14
14
|
"**/docs/**",
|
|
15
15
|
"**/test/**",
|
|
16
16
|
"**/tests/**",
|
|
17
|
+
"**/__tests__/**",
|
|
18
|
+
"**/testdata/**",
|
|
17
19
|
"**/fixtures/**",
|
|
18
20
|
"**/__fixtures__/**",
|
|
19
21
|
"**/vendor/**",
|
|
22
|
+
"**/vendored/**",
|
|
20
23
|
"**/third_party/**",
|
|
21
24
|
"**/node_modules/**",
|
|
22
25
|
"**/dist/**",
|
|
23
26
|
"**/build/**",
|
|
27
|
+
"out/**",
|
|
28
|
+
"target/**",
|
|
29
|
+
"**/.next/**",
|
|
24
30
|
"**/coverage/**",
|
|
25
31
|
"**/generated/**",
|
|
26
32
|
"**/*.generated.*",
|
|
27
33
|
"**/*.min.js",
|
|
28
34
|
"**/*.map",
|
|
29
|
-
"**/*.md"
|
|
35
|
+
"**/*.md",
|
|
36
|
+
"**/*.mdx",
|
|
37
|
+
"**/*.markdown",
|
|
38
|
+
"**/*.rst",
|
|
39
|
+
"**/*.test.*",
|
|
40
|
+
"**/*.spec.*",
|
|
41
|
+
"**/*_test.*",
|
|
42
|
+
"**/*_spec.*"
|
|
30
43
|
],
|
|
31
44
|
"content": [
|
|
32
45
|
"(?i)\\b(ponytail|caveman|yagni-oneliner):"
|
|
@@ -44,7 +57,7 @@
|
|
|
44
57
|
},
|
|
45
58
|
{
|
|
46
59
|
"id": "marker.ai-authored",
|
|
47
|
-
"version":
|
|
60
|
+
"version": 2,
|
|
48
61
|
"provider": "generic",
|
|
49
62
|
"category": "ai-marker",
|
|
50
63
|
"confidence": "low",
|
|
@@ -57,22 +70,35 @@
|
|
|
57
70
|
"**/docs/**",
|
|
58
71
|
"**/test/**",
|
|
59
72
|
"**/tests/**",
|
|
73
|
+
"**/__tests__/**",
|
|
74
|
+
"**/testdata/**",
|
|
60
75
|
"**/fixtures/**",
|
|
61
76
|
"**/__fixtures__/**",
|
|
62
77
|
"**/vendor/**",
|
|
78
|
+
"**/vendored/**",
|
|
63
79
|
"**/third_party/**",
|
|
64
80
|
"**/node_modules/**",
|
|
65
81
|
"**/dist/**",
|
|
66
82
|
"**/build/**",
|
|
83
|
+
"out/**",
|
|
84
|
+
"target/**",
|
|
85
|
+
"**/.next/**",
|
|
67
86
|
"**/coverage/**",
|
|
68
87
|
"**/generated/**",
|
|
69
88
|
"**/*.generated.*",
|
|
70
89
|
"**/*.min.js",
|
|
71
90
|
"**/*.map",
|
|
72
|
-
"**/*.md"
|
|
91
|
+
"**/*.md",
|
|
92
|
+
"**/*.mdx",
|
|
93
|
+
"**/*.markdown",
|
|
94
|
+
"**/*.rst",
|
|
95
|
+
"**/*.test.*",
|
|
96
|
+
"**/*.spec.*",
|
|
97
|
+
"**/*_test.*",
|
|
98
|
+
"**/*_spec.*"
|
|
73
99
|
],
|
|
74
100
|
"content": [
|
|
75
|
-
"(?i)\\b(
|
|
101
|
+
"(?i)\\b(generated by (ai|copilot|claude|chatgpt|codex))\\b"
|
|
76
102
|
]
|
|
77
103
|
},
|
|
78
104
|
"actions": {
|
package/rules/paths.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"id": "claude.local-settings",
|
|
4
|
-
"version":
|
|
4
|
+
"version": 2,
|
|
5
5
|
"provider": "claude-code",
|
|
6
6
|
"category": "local-settings",
|
|
7
7
|
"confidence": "high",
|
|
8
8
|
"kind": "path",
|
|
9
9
|
"match": {
|
|
10
|
+
"path_case": "insensitive",
|
|
10
11
|
"paths": [
|
|
11
12
|
".claude/settings.local.json",
|
|
12
13
|
"**/.claude/settings.local.json"
|
|
@@ -24,17 +25,16 @@
|
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
"id": "claude.session-state",
|
|
27
|
-
"version":
|
|
28
|
+
"version": 2,
|
|
28
29
|
"provider": "claude-code",
|
|
29
30
|
"category": "ephemeral-state",
|
|
30
31
|
"confidence": "high",
|
|
31
32
|
"kind": "path",
|
|
32
33
|
"match": {
|
|
34
|
+
"path_case": "insensitive",
|
|
33
35
|
"paths": [
|
|
34
36
|
".claude.json",
|
|
35
37
|
"**/.claude.json",
|
|
36
|
-
".claude/.credentials.json",
|
|
37
|
-
"**/.claude/.credentials.json",
|
|
38
38
|
".claude/session*.json",
|
|
39
39
|
"**/.claude/session*.json",
|
|
40
40
|
".claude/history*",
|
|
@@ -64,12 +64,13 @@
|
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
"id": "codex.session-state",
|
|
67
|
-
"version":
|
|
67
|
+
"version": 2,
|
|
68
68
|
"provider": "codex",
|
|
69
69
|
"category": "ephemeral-state",
|
|
70
70
|
"confidence": "high",
|
|
71
71
|
"kind": "path",
|
|
72
72
|
"match": {
|
|
73
|
+
"path_case": "insensitive",
|
|
73
74
|
"paths": [
|
|
74
75
|
".codex/sessions/**",
|
|
75
76
|
"**/.codex/sessions/**",
|
|
@@ -93,12 +94,13 @@
|
|
|
93
94
|
},
|
|
94
95
|
{
|
|
95
96
|
"id": "copilot.session-state",
|
|
96
|
-
"version":
|
|
97
|
+
"version": 3,
|
|
97
98
|
"provider": "copilot",
|
|
98
99
|
"category": "ephemeral-state",
|
|
99
100
|
"confidence": "high",
|
|
100
101
|
"kind": "path",
|
|
101
102
|
"match": {
|
|
103
|
+
"path_case": "insensitive",
|
|
102
104
|
"paths": [
|
|
103
105
|
".copilot/**",
|
|
104
106
|
"**/.copilot/**"
|
|
@@ -116,12 +118,13 @@
|
|
|
116
118
|
},
|
|
117
119
|
{
|
|
118
120
|
"id": "cursor.session-state",
|
|
119
|
-
"version":
|
|
121
|
+
"version": 2,
|
|
120
122
|
"provider": "cursor",
|
|
121
123
|
"category": "ephemeral-state",
|
|
122
124
|
"confidence": "medium",
|
|
123
125
|
"kind": "path",
|
|
124
126
|
"match": {
|
|
127
|
+
"path_case": "insensitive",
|
|
125
128
|
"paths": [
|
|
126
129
|
".cursor/session*",
|
|
127
130
|
"**/.cursor/session*",
|
|
@@ -143,12 +146,13 @@
|
|
|
143
146
|
},
|
|
144
147
|
{
|
|
145
148
|
"id": "aider.history",
|
|
146
|
-
"version":
|
|
149
|
+
"version": 3,
|
|
147
150
|
"provider": "aider",
|
|
148
151
|
"category": "ephemeral-state",
|
|
149
152
|
"confidence": "high",
|
|
150
153
|
"kind": "path",
|
|
151
154
|
"match": {
|
|
155
|
+
"path_case": "insensitive",
|
|
152
156
|
"paths": [
|
|
153
157
|
".aider.*",
|
|
154
158
|
"**/.aider.*"
|
|
@@ -166,12 +170,13 @@
|
|
|
166
170
|
},
|
|
167
171
|
{
|
|
168
172
|
"id": "specstory.history",
|
|
169
|
-
"version":
|
|
173
|
+
"version": 2,
|
|
170
174
|
"provider": "specstory",
|
|
171
175
|
"category": "ephemeral-state",
|
|
172
176
|
"confidence": "medium",
|
|
173
177
|
"kind": "path",
|
|
174
178
|
"match": {
|
|
179
|
+
"path_case": "insensitive",
|
|
175
180
|
"paths": [
|
|
176
181
|
".specstory/**",
|
|
177
182
|
"**/.specstory/**"
|
|
@@ -189,12 +194,13 @@
|
|
|
189
194
|
},
|
|
190
195
|
{
|
|
191
196
|
"id": "continue.sessions",
|
|
192
|
-
"version":
|
|
197
|
+
"version": 2,
|
|
193
198
|
"provider": "continue",
|
|
194
199
|
"category": "ephemeral-state",
|
|
195
200
|
"confidence": "medium",
|
|
196
201
|
"kind": "path",
|
|
197
202
|
"match": {
|
|
203
|
+
"path_case": "insensitive",
|
|
198
204
|
"paths": [
|
|
199
205
|
".continue/sessions/**",
|
|
200
206
|
"**/.continue/sessions/**"
|
|
@@ -212,7 +218,7 @@
|
|
|
212
218
|
},
|
|
213
219
|
{
|
|
214
220
|
"id": "secret.dotenv",
|
|
215
|
-
"version":
|
|
221
|
+
"version": 3,
|
|
216
222
|
"provider": "generic",
|
|
217
223
|
"category": "secret",
|
|
218
224
|
"confidence": "high",
|
|
@@ -226,16 +232,11 @@
|
|
|
226
232
|
"**/.env.*"
|
|
227
233
|
],
|
|
228
234
|
"except": [
|
|
229
|
-
"
|
|
230
|
-
"
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
"**/.env.template",
|
|
235
|
-
".env.dist",
|
|
236
|
-
"**/.env.dist",
|
|
237
|
-
".env.defaults",
|
|
238
|
-
"**/.env.defaults"
|
|
235
|
+
"**/*.example",
|
|
236
|
+
"**/*.sample",
|
|
237
|
+
"**/*.template",
|
|
238
|
+
"**/*.dist",
|
|
239
|
+
"**/*.defaults"
|
|
239
240
|
]
|
|
240
241
|
},
|
|
241
242
|
"actions": {
|
|
@@ -303,8 +304,33 @@
|
|
|
303
304
|
]
|
|
304
305
|
},
|
|
305
306
|
{
|
|
306
|
-
"id": "
|
|
307
|
+
"id": "secret.claude-credentials",
|
|
307
308
|
"version": 1,
|
|
309
|
+
"provider": "claude-code",
|
|
310
|
+
"category": "secret",
|
|
311
|
+
"confidence": "high",
|
|
312
|
+
"kind": "path",
|
|
313
|
+
"match": {
|
|
314
|
+
"path_case": "insensitive",
|
|
315
|
+
"paths": [
|
|
316
|
+
".claude/.credentials.json",
|
|
317
|
+
"**/.claude/.credentials.json"
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
"actions": {
|
|
321
|
+
"clean": "block",
|
|
322
|
+
"strict": "block",
|
|
323
|
+
"compliance": "block"
|
|
324
|
+
},
|
|
325
|
+
"reason": "This file holds an OAuth access token and must never be committed.",
|
|
326
|
+
"remediation": [
|
|
327
|
+
"git restore --staged <path>",
|
|
328
|
+
"Revoke the token if it already reached a commit."
|
|
329
|
+
]
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"id": "generic.agent-instructions",
|
|
333
|
+
"version": 2,
|
|
308
334
|
"provider": "generic",
|
|
309
335
|
"category": "ambiguous-instructions",
|
|
310
336
|
"confidence": "medium",
|
|
@@ -328,18 +354,19 @@
|
|
|
328
354
|
},
|
|
329
355
|
"reason": "Agent instruction files may be intentional team config. Review before committing.",
|
|
330
356
|
"remediation": [
|
|
331
|
-
"aimhooman
|
|
357
|
+
"aimhooman review <path> --head <commit> --reason \"shared team config\"",
|
|
332
358
|
"or unstage if it is personal"
|
|
333
359
|
]
|
|
334
360
|
},
|
|
335
361
|
{
|
|
336
362
|
"id": "generic.project-policy",
|
|
337
|
-
"version":
|
|
363
|
+
"version": 2,
|
|
338
364
|
"provider": "aimhooman",
|
|
339
365
|
"category": "policy-config",
|
|
340
366
|
"confidence": "high",
|
|
341
367
|
"kind": "path",
|
|
342
368
|
"match": {
|
|
369
|
+
"path_case": "insensitive",
|
|
343
370
|
"paths": [
|
|
344
371
|
".aimhooman.json",
|
|
345
372
|
"**/.aimhooman.json"
|
|
@@ -352,54 +379,54 @@
|
|
|
352
379
|
},
|
|
353
380
|
"reason": "Versioned enforcement policy changes require explicit human review.",
|
|
354
381
|
"remediation": [
|
|
355
|
-
"aimhooman
|
|
382
|
+
"aimhooman review .aimhooman.json --head <commit> --reason \"reviewed team policy change\"",
|
|
356
383
|
"or unstage the policy change"
|
|
357
384
|
]
|
|
358
385
|
},
|
|
359
386
|
{
|
|
360
387
|
"id": "playwright-mcp.state",
|
|
361
|
-
"version":
|
|
388
|
+
"version": 2,
|
|
362
389
|
"provider": "playwright-mcp",
|
|
363
390
|
"category": "ephemeral-state",
|
|
364
391
|
"confidence": "high",
|
|
365
392
|
"kind": "path",
|
|
366
|
-
"match": { "paths": [".playwright-mcp/**", "**/.playwright-mcp/**"] },
|
|
393
|
+
"match": { "path_case": "insensitive", "paths": [".playwright-mcp/**", "**/.playwright-mcp/**"] },
|
|
367
394
|
"actions": { "clean": "block", "strict": "block", "compliance": "block" },
|
|
368
395
|
"reason": "Playwright MCP session artifacts are local, not repository content.",
|
|
369
396
|
"remediation": ["git restore --staged <path>"]
|
|
370
397
|
},
|
|
371
398
|
{
|
|
372
399
|
"id": "remember.state",
|
|
373
|
-
"version":
|
|
400
|
+
"version": 2,
|
|
374
401
|
"provider": "remember",
|
|
375
402
|
"category": "ephemeral-state",
|
|
376
403
|
"confidence": "high",
|
|
377
404
|
"kind": "path",
|
|
378
|
-
"match": { "paths": [".remember/**", "**/.remember/**"] },
|
|
405
|
+
"match": { "path_case": "insensitive", "paths": [".remember/**", "**/.remember/**"] },
|
|
379
406
|
"actions": { "clean": "block", "strict": "block", "compliance": "block" },
|
|
380
407
|
"reason": "Remember second-brain data is local, not repository content.",
|
|
381
408
|
"remediation": ["git restore --staged <path>"]
|
|
382
409
|
},
|
|
383
410
|
{
|
|
384
411
|
"id": "superpowers.state",
|
|
385
|
-
"version":
|
|
412
|
+
"version": 2,
|
|
386
413
|
"provider": "superpowers",
|
|
387
414
|
"category": "ephemeral-state",
|
|
388
415
|
"confidence": "high",
|
|
389
416
|
"kind": "path",
|
|
390
|
-
"match": { "paths": [".superpowers/**", "**/.superpowers/**"] },
|
|
417
|
+
"match": { "path_case": "insensitive", "paths": [".superpowers/**", "**/.superpowers/**"] },
|
|
391
418
|
"actions": { "clean": "block", "strict": "block", "compliance": "block" },
|
|
392
419
|
"reason": "Superpowers plugin state is local, not repository content.",
|
|
393
420
|
"remediation": ["git restore --staged <path>"]
|
|
394
421
|
},
|
|
395
422
|
{
|
|
396
423
|
"id": "agent.state",
|
|
397
|
-
"version":
|
|
424
|
+
"version": 3,
|
|
398
425
|
"provider": "generic",
|
|
399
426
|
"category": "ephemeral-state",
|
|
400
427
|
"confidence": "high",
|
|
401
428
|
"kind": "path",
|
|
402
|
-
"match": { "paths": [".agent/**", "**/.agent/**"] },
|
|
429
|
+
"match": { "path_case": "insensitive", "paths": [".agent/**", "**/.agent/**"] },
|
|
403
430
|
"actions": { "clean": "block", "strict": "block", "compliance": "block" },
|
|
404
431
|
"reason": "Generic agent state is local, not repository content.",
|
|
405
432
|
"remediation": ["git restore --staged <path>"]
|
package/rules/secrets.json
CHANGED
|
@@ -74,7 +74,8 @@
|
|
|
74
74
|
"kind": "code",
|
|
75
75
|
"match": {
|
|
76
76
|
"content": [
|
|
77
|
-
"\\b(?:gh[pousr]_[A-Za-z0-9]{36,255}|github_pat_[A-Za-z0-9_]{60,255}|glpat-[A-Za-z0-9_-]{20,255}|npm_[A-Za-z0-9]{36,255}|xox[baprs]-[A-Za-z0-9-]{20,255})\\b"
|
|
77
|
+
"\\b(?:gh[pousr]_[A-Za-z0-9]{36,255}|github_pat_[A-Za-z0-9_]{60,255}|glpat-[A-Za-z0-9_-]{20,255}|npm_[A-Za-z0-9]{36,255}|xox[baprs]-[A-Za-z0-9-]{20,255})\\b",
|
|
78
|
+
"\\b(?:sk-ant-api[0-9]{2}-[A-Za-z0-9_-]{80,255}|sk-proj-[A-Za-z0-9_-]{40,255}|AIza[A-Za-z0-9_-]{35}|(?:sk|rk)_live_[A-Za-z0-9]{20,255}|hf_[A-Za-z0-9]{34}|SG\\.[A-Za-z0-9_-]{20,255}\\.[A-Za-z0-9_-]{40,255})\\b"
|
|
78
79
|
]
|
|
79
80
|
},
|
|
80
81
|
"actions": {
|
|
@@ -17,7 +17,7 @@ This repository uses aimhooman to keep AI tooling artifacts out of Git history.
|
|
|
17
17
|
Its policy:
|
|
18
18
|
|
|
19
19
|
- Never stage or commit local AI session/state files. Examples include
|
|
20
|
-
`.claude.json`, `.claude
|
|
20
|
+
`.claude.json`, `.claude/session*.json`,
|
|
21
21
|
`.claude/history*`, `.claude/todos/*`, `.claude/shell-snapshots/*`,
|
|
22
22
|
`.claude/statsig/*`, `.claude/projects/*`, `.claude/logs/*`,
|
|
23
23
|
`.codex/sessions/*`, `.codex/history*`, `.codex/log/*`, `.codex/logs/*`,
|
|
@@ -27,8 +27,10 @@ Its policy:
|
|
|
27
27
|
exhaustive; the packaged `rules/paths.json` is the detection source of truth.
|
|
28
28
|
If one is staged, unstage it and keep it out of Git instead.
|
|
29
29
|
- Never commit secrets: a real `.env` (not `.env.example`), private keys
|
|
30
|
-
(`id_rsa` and files containing a private-key header), `.aws/credentials`,
|
|
31
|
-
service-account keys
|
|
30
|
+
(`id_rsa` and files containing a private-key header), `.aws/credentials`,
|
|
31
|
+
`.claude/.credentials.json`, service-account keys, or a provider API key
|
|
32
|
+
(GitHub, GitLab, npm, Slack, Anthropic, OpenAI, Google, Stripe, Hugging Face,
|
|
33
|
+
SendGrid). Public certificates are allowed.
|
|
32
34
|
- Never add AI attribution to commit messages: no `Co-authored-by` trailer naming
|
|
33
35
|
an AI (Claude, Copilot, Codex), no "Generated with/by <AI>" lines, no AI-service
|
|
34
36
|
noreply emails. A commit message reads as if a human wrote it.
|
package/src/atomic-write.mjs
CHANGED
|
@@ -290,7 +290,17 @@ export function withLock(lockPath, fn, options = {}) {
|
|
|
290
290
|
if (attempt + 1 < retries) waitForLock(retryDelayMs);
|
|
291
291
|
}
|
|
292
292
|
if (!held) {
|
|
293
|
-
|
|
293
|
+
// Name the queue, not just the lock. This scheme writes no file at lockPath;
|
|
294
|
+
// only the legacy holder checked above would, so a reader of this error
|
|
295
|
+
// usually goes looking for a path that is not there. What blocks is a
|
|
296
|
+
// candidate in the queue directory, retained because its owner cannot be
|
|
297
|
+
// disproved, and a holder killed outright never reaches the finally below
|
|
298
|
+
// that would have removed its own.
|
|
299
|
+
throw new Error(
|
|
300
|
+
`cannot acquire state lock "${lockPath}" after ${retries} attempts. Contenders `
|
|
301
|
+
+ `queue in "${queueDir}"; if no other aimhooman command is running, a candidate `
|
|
302
|
+
+ 'there outlived the process that published it, and removing that directory clears it',
|
|
303
|
+
);
|
|
294
304
|
}
|
|
295
305
|
return fn();
|
|
296
306
|
} catch (error) {
|
package/src/gitx.mjs
CHANGED
|
@@ -200,7 +200,7 @@ export function unmergedPaths(repo) {
|
|
|
200
200
|
export function stagedRenameSources(repo, destinations) {
|
|
201
201
|
const selected = new Set(destinations);
|
|
202
202
|
if (!selected.size) return [];
|
|
203
|
-
const entries = diffEntries(repo, ['--cached'], 1);
|
|
203
|
+
const entries = diffEntries(repo, ['--cached'], '1%');
|
|
204
204
|
const sources = new Set(entries
|
|
205
205
|
.filter((entry) => entry.status === 'R' && selected.has(entry.path) && entry.sourcePath)
|
|
206
206
|
.map((entry) => entry.sourcePath));
|
|
@@ -210,7 +210,9 @@ export function stagedRenameSources(repo, destinations) {
|
|
|
210
210
|
// there is no exact source to recover. When a blocked destination remains an
|
|
211
211
|
// add even at the lowest similarity threshold, conservatively restore every
|
|
212
212
|
// staged deletion. This may unstage an unrelated deletion, but it never lets
|
|
213
|
-
// automatic repair silently commit half of a possible rename.
|
|
213
|
+
// automatic repair silently commit half of a possible rename. That threshold
|
|
214
|
+
// carries a % sign because git reads a bare 1 as the fraction 0.1, so -M1
|
|
215
|
+
// asks for 10% and leaves a 3%-similar rename here as an add plus a delete.
|
|
214
216
|
if (entries.some((entry) => entry.status === 'A' && selected.has(entry.path))) {
|
|
215
217
|
for (const entry of entries) {
|
|
216
218
|
if (entry.status === 'D') sources.add(entry.path);
|
|
@@ -226,8 +228,13 @@ export function readStagedPath(repo, path) {
|
|
|
226
228
|
const target = 'staged';
|
|
227
229
|
let records;
|
|
228
230
|
try {
|
|
231
|
+
// The --literal-pathspecs flag rather than :(top,literal) magic: magic
|
|
232
|
+
// parsing is disabled by GIT_LITERAL_PATHSPECS in the environment, which
|
|
233
|
+
// would leave the pathspec a literal filename no repository contains, so
|
|
234
|
+
// git matched nothing and the read reported the policy absent. cwd is
|
|
235
|
+
// already repo.root, which is what the top magic was for.
|
|
229
236
|
records = nulStrings(gitBuf([
|
|
230
|
-
'ls-files', '--stage', '-z', '--',
|
|
237
|
+
'--literal-pathspecs', 'ls-files', '--stage', '-z', '--', path,
|
|
231
238
|
], repo.root));
|
|
232
239
|
} catch (error) {
|
|
233
240
|
throw new GitTargetReadError(target, path, gitErrorDetail(error), error);
|
|
@@ -274,7 +281,7 @@ export function readCommitPath(repo, revision, path) {
|
|
|
274
281
|
let records;
|
|
275
282
|
try {
|
|
276
283
|
records = nulStrings(gitBuf([
|
|
277
|
-
'ls-tree', '--full-tree', '-z', oid, '--',
|
|
284
|
+
'--literal-pathspecs', 'ls-tree', '--full-tree', '-z', oid, '--', path,
|
|
278
285
|
], repo.root));
|
|
279
286
|
} catch (error) {
|
|
280
287
|
throw new GitTargetReadError(target, path, gitErrorDetail(error), error);
|
|
@@ -483,8 +490,13 @@ export function unstagePaths(repo, paths) {
|
|
|
483
490
|
'--pathspec-from-file=-', '--pathspec-file-nul',
|
|
484
491
|
], opts);
|
|
485
492
|
} else {
|
|
493
|
+
// -f waives only the check that the staged blob still matches the file
|
|
494
|
+
// on disk. With no HEAD that check has nothing else to pass against, so
|
|
495
|
+
// an artifact appended to between `git add` and `git commit` could never
|
|
496
|
+
// be unstaged. --cached leaves the worktree alone either way, and the
|
|
497
|
+
// sibling `restore --staged` branch enforces no equivalent check.
|
|
486
498
|
execFileSync('git', [
|
|
487
|
-
'--literal-pathspecs', 'rm', '--cached', '--quiet', '--ignore-unmatch',
|
|
499
|
+
'--literal-pathspecs', 'rm', '--cached', '-f', '--quiet', '--ignore-unmatch',
|
|
488
500
|
'--pathspec-from-file=-', '--pathspec-file-nul',
|
|
489
501
|
], opts);
|
|
490
502
|
}
|
package/src/history-scan.mjs
CHANGED
|
@@ -13,6 +13,10 @@ function gitBuffer(repo, args, input) {
|
|
|
13
13
|
encoding: 'buffer',
|
|
14
14
|
maxBuffer: 128 * 1024 * 1024,
|
|
15
15
|
timeout: GIT_TIMEOUT_MS,
|
|
16
|
+
// Same reason as gitBuf in gitx.mjs: without an explicit stdio,
|
|
17
|
+
// execFileSync echoes the child's stderr before it checks the exit
|
|
18
|
+
// status, so git's raw output reaches the terminal even on success.
|
|
19
|
+
stdio: [input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'],
|
|
16
20
|
...(input === undefined ? {} : { input }),
|
|
17
21
|
});
|
|
18
22
|
}
|