@rmyndharis/aimhooman 0.1.6 → 0.1.7
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +39 -0
- package/README.md +2 -2
- package/bin/aimhooman.mjs +50 -6
- package/docs/design/frictionless-enforcement.md +7 -4
- package/package.json +1 -1
- package/src/hook.mjs +60 -3
- package/src/scan-target.mjs +23 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,45 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.7] - 2026-07-18
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- The final reference guard no longer holds a branch hostage to a file already
|
|
13
|
+
in its history. A commit was scanned against the full tree it inherited, so a
|
|
14
|
+
path allowed in once and then un-allowed blocked every later commit on the
|
|
15
|
+
branch, even one-line edits to an unrelated file — and the only way out was to
|
|
16
|
+
rewrite history. The guard now judges a commit by what it changes: a newly
|
|
17
|
+
staged `.env` still stops the commit, while a file the commit merely carries
|
|
18
|
+
forward from its parent stays silent. A secret already in history is not
|
|
19
|
+
forgotten — `aimhooman check --tracked` still names it — but it no longer
|
|
20
|
+
bricks the branch.
|
|
21
|
+
- Imported history no longer trips the attribution guard. `gh pr checkout` and
|
|
22
|
+
`git fetch` bring in other people's commits, whose messages a local developer
|
|
23
|
+
cannot edit; scanning them for AI co-author trailers blocked the checkout
|
|
24
|
+
whenever a PR commit carried one. The guard now scopes attribution and marker
|
|
25
|
+
rules to commits written in the repository (a plain commit, an `--amend`, a
|
|
26
|
+
local merge), and leaves imported commits' messages alone. A locally authored
|
|
27
|
+
`git commit --no-verify` that smuggles the same trailer is still stopped at the
|
|
28
|
+
final guard.
|
|
29
|
+
- `aimhooman status` no longer advertises a profile the hooks are not applying.
|
|
30
|
+
The enforcing guards resolve the project policy from the index, so a worktree
|
|
31
|
+
`.aimhooman.json` that has not been `git add`ed was invisible to them — yet
|
|
32
|
+
`status` reported its profile as active. `status` now shows the staged profile
|
|
33
|
+
the hooks actually enforce, prints the worktree value alongside when the two
|
|
34
|
+
differ, and names the remedy (`git add .aimhooman.json`).
|
|
35
|
+
- A pipeline into a shell with no commit in it is still refused, but the reason
|
|
36
|
+
no longer tells the developer to retry a commit they never wrote. The deny
|
|
37
|
+
text for `echo x | bash` and similar now names the real shape — a pipeline
|
|
38
|
+
whose sink can run arbitrary commands — instead of reusing the commit-themed
|
|
39
|
+
message.
|
|
40
|
+
- A timing or scheduling prefix no longer reads as a hook bypass. `time`,
|
|
41
|
+
`timeout`, `nice`, and `ionice` run the inner command with the same argv in the
|
|
42
|
+
same place, so `time git commit` is judged like `git commit` instead of being
|
|
43
|
+
refused as opaque shell indirection. The carve-out only applies when nothing
|
|
44
|
+
else injects risk; a `--no-verify` or a hooks-path override wrapped in the
|
|
45
|
+
prefix is still caught.
|
|
46
|
+
|
|
8
47
|
## [0.1.6] - 2026-07-18
|
|
9
48
|
|
|
10
49
|
### Fixed
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
11
|
<img src="https://img.shields.io/github/actions/workflow/status/rmyndharis/aimhooman/test.yml?branch=main&label=CI" alt="CI">
|
|
12
|
-
<img src="https://img.shields.io/badge/version-v0.1.
|
|
12
|
+
<img src="https://img.shields.io/badge/version-v0.1.7-blue" alt="v0.1.7">
|
|
13
13
|
<img src="https://img.shields.io/badge/node-%E2%89%A522.8-339933?logo=node.js&logoColor=white" alt="Node 22.8+">
|
|
14
14
|
<img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="Zero dependencies">
|
|
15
15
|
<img src="https://img.shields.io/badge/license-MIT-111111" alt="MIT">
|
|
@@ -76,7 +76,7 @@ flowchart TD
|
|
|
76
76
|
DIRECT([Sequencer or direct ref path<br/>cherry-pick · rebase · fetch · worktree · update-ref]) --> REF
|
|
77
77
|
PRE -->|clean: safe repair| MSG["commit-msg snapshots would-be tree,<br/>runs predecessor, then checks<br/>the message and pinned full tree"]
|
|
78
78
|
PRE -->|strict violation, incomplete scan,<br/>or failed repair| BLOCK([Operation stops])
|
|
79
|
-
MSG -->|message and tree accepted| REF["reference-transaction prepared<br/>
|
|
79
|
+
MSG -->|message and tree accepted| REF["reference-transaction prepared<br/>scans what each introduced commit changes<br/>(messages of locally authored commits only)"]
|
|
80
80
|
MSG -->|unsafe or unrepairable| BLOCK
|
|
81
81
|
REF -->|accepted| SHIP([Ref update commits])
|
|
82
82
|
REF -->|violation or incomplete scan| BLOCK
|
package/bin/aimhooman.mjs
CHANGED
|
@@ -599,8 +599,23 @@ function cmdRefcheck(args) {
|
|
|
599
599
|
let commits;
|
|
600
600
|
try {
|
|
601
601
|
const contextsByCommit = new Map();
|
|
602
|
+
// A commit's message belongs to whoever wrote it. Attribution and marker
|
|
603
|
+
// rules police the text a local developer can edit, so they are scoped
|
|
604
|
+
// to commits authored here: an update that introduces exactly one new
|
|
605
|
+
// commit on top of a non-zero old tip (a plain commit, an --amend, or a
|
|
606
|
+
// local --no-ff merge of an already-gated branch). Anything else — a new
|
|
607
|
+
// branch pulled in by `gh pr checkout` or `git fetch`, a merge of fetched
|
|
608
|
+
// history — imports other people's commit text the developer cannot
|
|
609
|
+
// change, and scanning it only blocks the review.
|
|
610
|
+
const localAuthorTips = new Set();
|
|
602
611
|
for (const update of updates) {
|
|
603
|
-
|
|
612
|
+
const introduced = introducedCommits(repo, [update]);
|
|
613
|
+
if (!/^0+$/.test(update.oldObjectId)
|
|
614
|
+
&& introduced.length === 1
|
|
615
|
+
&& introduced[0] === update.newObjectId) {
|
|
616
|
+
localAuthorTips.add(update.newObjectId);
|
|
617
|
+
}
|
|
618
|
+
for (const revision of introduced) {
|
|
604
619
|
const contexts = contextsByCommit.get(revision) || [];
|
|
605
620
|
contexts.push({
|
|
606
621
|
head: update.newObjectId,
|
|
@@ -624,14 +639,18 @@ function cmdRefcheck(args) {
|
|
|
624
639
|
contextsByCommit.set(revision, contexts);
|
|
625
640
|
}
|
|
626
641
|
}
|
|
627
|
-
commits = [...contextsByCommit]
|
|
642
|
+
commits = [...contextsByCommit].map(([revision, reviewContexts]) => [
|
|
643
|
+
revision,
|
|
644
|
+
reviewContexts,
|
|
645
|
+
localAuthorTips.has(revision),
|
|
646
|
+
]);
|
|
628
647
|
}
|
|
629
648
|
catch (error) {
|
|
630
649
|
console.error(`aimhooman: cannot resolve proposed commits: ${error.message}`);
|
|
631
650
|
return 30;
|
|
632
651
|
}
|
|
633
652
|
const limits = scanLimits();
|
|
634
|
-
for (const [revision, reviewContexts] of commits) {
|
|
653
|
+
for (const [revision, reviewContexts, authoredLocally] of commits) {
|
|
635
654
|
let scan;
|
|
636
655
|
try {
|
|
637
656
|
scan = scanGitTarget(repo, {
|
|
@@ -640,6 +659,7 @@ function cmdRefcheck(args) {
|
|
|
640
659
|
reviewContexts,
|
|
641
660
|
policyMigrationContexts: reviewContexts,
|
|
642
661
|
limits,
|
|
662
|
+
messageScope: authoredLocally ? 'commit' : 'changes-only',
|
|
643
663
|
});
|
|
644
664
|
}
|
|
645
665
|
catch (error) {
|
|
@@ -927,9 +947,17 @@ function cmdStatus(args) {
|
|
|
927
947
|
return 30;
|
|
928
948
|
}
|
|
929
949
|
let policy;
|
|
950
|
+
let stagedPolicy;
|
|
930
951
|
let overrides;
|
|
931
952
|
try {
|
|
953
|
+
// `policy` describes the worktree file as written; `stagedPolicy` is the
|
|
954
|
+
// one the enforcing hooks actually read (they resolve from the index, so
|
|
955
|
+
// a worktree profile that has not been staged is invisible to them).
|
|
956
|
+
// Reporting the worktree value alone used to advertise a profile the
|
|
957
|
+
// guard was not applying, so the two are compared below and the
|
|
958
|
+
// mismatch is named when they disagree.
|
|
932
959
|
policy = resolvePolicy(repo, { target: 'worktree' });
|
|
960
|
+
stagedPolicy = resolvePolicy(repo, { target: 'staged' });
|
|
933
961
|
overrides = loadOverrides(repo.stateDir);
|
|
934
962
|
} catch (e) {
|
|
935
963
|
console.error(`aimhooman: cannot load enforcement state: ${e.message}`);
|
|
@@ -952,9 +980,25 @@ function cmdStatus(args) {
|
|
|
952
980
|
excludeError = e.message;
|
|
953
981
|
excludes = { current: false, installed: false };
|
|
954
982
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
983
|
+
// The pre-commit and reference-transaction guards resolve the policy from
|
|
984
|
+
// the index, so the staged profile is what is actually enforced. Report it
|
|
985
|
+
// first; the worktree file is shown alongside so an edit that has not been
|
|
986
|
+
// staged (or a file that was never `git add`ed) cannot masquerade as active.
|
|
987
|
+
const policyLabelOf = (resolved) => (
|
|
988
|
+
resolved.source === 'worktree-policy'
|
|
989
|
+
|| resolved.source === 'staged-policy'
|
|
990
|
+
|| resolved.source === 'commit-policy'
|
|
991
|
+
) ? 'project' : resolved.source;
|
|
992
|
+
const enforced = stagedPolicy.profile;
|
|
993
|
+
const enforcedLabel = policyLabelOf(stagedPolicy);
|
|
994
|
+
const worktreeLabel = policyLabelOf(policy);
|
|
995
|
+
const policyDrift = policy.profile !== stagedPolicy.profile
|
|
996
|
+
|| policy.policy_object_id !== stagedPolicy.policy_object_id;
|
|
997
|
+
console.log(`profile: ${enforced}${policyDrift ? ` (worktree: ${policy.profile})` : ''}`);
|
|
998
|
+
console.log(`policy: ${enforcedLabel} (${stagedPolicy.source}, object=${stagedPolicy.policy_object_id || 'none'})${policyDrift ? `; worktree ${worktreeLabel} (${policy.source}, object=${policy.policy_object_id || 'none'})` : ''}`);
|
|
999
|
+
if (policyDrift) {
|
|
1000
|
+
console.log(`warning: worktree .aimhooman.json is not staged, so the hooks enforce the staged profile (${enforced}); run \`git add .aimhooman.json\` to apply ${policy.profile}`);
|
|
1001
|
+
}
|
|
958
1002
|
console.log(`hooks: ${hooksComplete ? installed.join(', ') : installed.length ? `${installed.join(', ')} (incomplete; run: aimhooman init)` : 'not installed (run: aimhooman init)'}`);
|
|
959
1003
|
for (const hook of hooks) {
|
|
960
1004
|
console.log(`hook ${hook.name}: ${hook.managed ? 'managed' : 'not managed'}, ${hook.executable ? 'executable' : 'not executable'}, ${hook.reachable ? 'reachable' : hook.reason}`);
|
|
@@ -55,15 +55,18 @@ enforcement requires local or global hook setup.
|
|
|
55
55
|
| 2. Agent guard | PreToolUse reports paths and rejects unprovable protected Git mutations | plugin | advisory for paths; fail-closed for boundary bypass |
|
|
56
56
|
| 3. Strict policy | `strict` profile blocks instead of repairing (exit 10) | both | block |
|
|
57
57
|
| 4. Pinned tree | `commit-msg` checks the exact would-be tree and message | git hook | blocks a remaining violation or incomplete scan |
|
|
58
|
-
| 5. Final ref check | prepared `reference-transaction`
|
|
58
|
+
| 5. Final ref check | prepared `reference-transaction` scans what each commit introduced to `HEAD` or a branch changes (and the message of commits authored locally) | git hook | blocks a violation or incomplete scan |
|
|
59
59
|
|
|
60
60
|
Default profile `clean` uses layers 0, 1, 2, 4, and 5. Successful repair keeps the
|
|
61
61
|
ordinary path low-friction. The pinned-tree and final-ref scans deliberately stop if
|
|
62
|
-
a block remains
|
|
63
|
-
|
|
62
|
+
a block remains on a path the commit actually changes, or if any scan is incomplete.
|
|
63
|
+
A file already in history is not re-tried on every later commit: the final ref check
|
|
64
|
+
judges a commit by its change set, so an inherited path no longer blocks unrelated
|
|
65
|
+
work. Use `aimhooman check --tracked` (or scan the PR range in CI) to surface legacy
|
|
66
|
+
secrets without bricking the branch.
|
|
64
67
|
Git 2.54 also emits an earlier `preparing` reference-transaction callback. The
|
|
65
68
|
hook checks dispatcher integrity there without scanning unresolved references,
|
|
66
|
-
then keeps the
|
|
69
|
+
then keeps the scan veto at `prepared`, after Git has locked them.
|
|
67
70
|
|
|
68
71
|
### Frictionless agent guard (the everyday commands run)
|
|
69
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmyndharis/aimhooman",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "AI works. Hoomans ship. Keep AI session files, secrets, and attribution out of your commits.",
|
|
5
5
|
"homepage": "https://github.com/rmyndharis/aimhooman#readme",
|
|
6
6
|
"repository": {
|
package/src/hook.mjs
CHANGED
|
@@ -443,6 +443,14 @@ function hookPreToolUse(input) {
|
|
|
443
443
|
: '--no-verify or shell indirection bypasses the pre-commit guard';
|
|
444
444
|
const unmodelledPrefixReason =
|
|
445
445
|
'aimhooman cannot verify the final staged snapshot or Git hooks after an earlier unmodelled command; run that command separately, then retry the commit.';
|
|
446
|
+
// A pipeline whose sink can execute code (a shell, an interpreter fed on
|
|
447
|
+
// stdin) can hide a commit or run arbitrary commands, and there is no argv
|
|
448
|
+
// to read a --no-verify out of. The earlier message reused the commit text
|
|
449
|
+
// above, which told a developer to "retry the commit" for a command that
|
|
450
|
+
// was not a commit at all — this names the real shape instead.
|
|
451
|
+
const opaquePipelineReason = commit
|
|
452
|
+
? unmodelledPrefixReason
|
|
453
|
+
: 'aimhooman cannot prove this pipeline is read-only: a shell or code-executing segment can hide a commit or run arbitrary commands. Drop that segment (for example the `| bash`) and run the pieces separately.';
|
|
446
454
|
const blocks = [];
|
|
447
455
|
// potentialCommit treats a command as leading to a commit. uncertainShell
|
|
448
456
|
// was too broad: it flagged any pipe, so a benign read-only pipeline
|
|
@@ -522,7 +530,7 @@ function hookPreToolUse(input) {
|
|
|
522
530
|
if (profile !== 'strict' && potentialCommit && prefixHookBypass) {
|
|
523
531
|
return emitDecision(
|
|
524
532
|
'deny',
|
|
525
|
-
|
|
533
|
+
opaquePipelineReason,
|
|
526
534
|
);
|
|
527
535
|
}
|
|
528
536
|
// Nothing found and nothing to object to, so emit nothing and leave the
|
|
@@ -552,7 +560,7 @@ function hookPreToolUse(input) {
|
|
|
552
560
|
if (profile !== 'strict' && potentialCommit && prefixHookBypass) {
|
|
553
561
|
return emitDecision(
|
|
554
562
|
'deny',
|
|
555
|
-
|
|
563
|
+
opaquePipelineReason,
|
|
556
564
|
);
|
|
557
565
|
}
|
|
558
566
|
const guarded = repo && !bypassed && installedHooks(repo).includes('pre-commit');
|
|
@@ -861,6 +869,41 @@ export function parseGit(cmd, initialCwd = process.cwd()) {
|
|
|
861
869
|
environmentRisk,
|
|
862
870
|
'git',
|
|
863
871
|
);
|
|
872
|
+
// A passthrough prefix (time, timeout, nice, ...) execs the inner
|
|
873
|
+
// command with the same argv in the same place, so it cannot
|
|
874
|
+
// inject a flag or hide a --no-verify the way eval/sudo/bash -c
|
|
875
|
+
// can. Re-parse the inner command from its `git` token so the
|
|
876
|
+
// commit is judged exactly as if the prefix were not there
|
|
877
|
+
// (--no-verify still denied, a clean commit still allowed). The
|
|
878
|
+
// carve-out only applies when nothing else injects risk;
|
|
879
|
+
// otherwise the wrapper falls back to the closed uncertain path.
|
|
880
|
+
const passthroughExecutable = PASSTHROUGH_PREFIX_EXECUTORS.has(shellExecutable(g0))
|
|
881
|
+
&& environmentRisk.length === 0
|
|
882
|
+
&& !targetEnvironmentRisk
|
|
883
|
+
&& !indirectTargetEnvironmentRisk
|
|
884
|
+
&& !commandTargetUncertain
|
|
885
|
+
&& !indirect.targetUncertain
|
|
886
|
+
&& !indirect.pathDialectUncertain
|
|
887
|
+
&& !commandPathDialectUncertain
|
|
888
|
+
&& !unwrapped.uncertain
|
|
889
|
+
? shellExecutable(g0)
|
|
890
|
+
: null;
|
|
891
|
+
if (passthroughExecutable) {
|
|
892
|
+
const gitIndex = toks.findIndex((token, index) => (
|
|
893
|
+
index > 0 && isGitExecutable(token.replace(/^\(+/, '').replace(/\)+$/, ''))
|
|
894
|
+
));
|
|
895
|
+
if (gitIndex > 0) {
|
|
896
|
+
const inner = parseGit(toks.slice(gitIndex).join(' '), unwrapped.cwd);
|
|
897
|
+
commit ||= inner.commit;
|
|
898
|
+
noVerify ||= inner.noVerify;
|
|
899
|
+
bypassHooks ||= inner.bypassHooks;
|
|
900
|
+
uncertainShell ||= inner.uncertainShell;
|
|
901
|
+
addPaths.push(...inner.addPaths);
|
|
902
|
+
commands.push(...inner.commands);
|
|
903
|
+
sawAdd ||= inner.addPaths.length > 0;
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
864
907
|
commit = true;
|
|
865
908
|
uncertainShell = true;
|
|
866
909
|
commands.push({
|
|
@@ -883,7 +926,8 @@ export function parseGit(cmd, initialCwd = process.cwd()) {
|
|
|
883
926
|
|| indirect.pathDialectUncertain,
|
|
884
927
|
});
|
|
885
928
|
}
|
|
886
|
-
if (!READ_ONLY_SHELL_COMMANDS.has(basename(g0))
|
|
929
|
+
if (!READ_ONLY_SHELL_COMMANDS.has(basename(g0))
|
|
930
|
+
&& !PASSTHROUGH_PREFIX_EXECUTORS.has(shellExecutable(g0))) {
|
|
887
931
|
prefixIndexMutationRisk ||= commandMayReplaceIndex(toks, unit.text);
|
|
888
932
|
prefixHooksRisk ||= commandMayTouchHooks(toks, unit.text);
|
|
889
933
|
prefixRisk = true;
|
|
@@ -2687,6 +2731,19 @@ const UNCERTAIN_TARGET_INDIRECT_EXECUTORS = new Set([
|
|
|
2687
2731
|
'systemd-run', 'unshare', 'watch', 'wsl', 'xargs',
|
|
2688
2732
|
]);
|
|
2689
2733
|
|
|
2734
|
+
// Passthrough prefixes exec the inner command verbatim: the same argv, the same
|
|
2735
|
+
// cwd, the same target. `time`, `timeout`, `nice`, `ionice` and friends only
|
|
2736
|
+
// measure or schedule — they cannot inject a flag, rewrite the command line, or
|
|
2737
|
+
// hide a --no-verify the way eval/sudo/bash -c can. An indirect commit seen
|
|
2738
|
+
// through one of these is therefore treated as direct, so `time git commit` is
|
|
2739
|
+
// judged like `git commit` instead of being refused as opaque indirection. The
|
|
2740
|
+
// carve-out only applies when nothing else is risky (no environment override,
|
|
2741
|
+
// no uncertain target); otherwise the wrapper falls back to the closed path.
|
|
2742
|
+
const PASSTHROUGH_PREFIX_EXECUTORS = new Set([
|
|
2743
|
+
'time', 'timeout', 'nice', 'ionice', 'chrt', 'taskset', 'numactl',
|
|
2744
|
+
'stdbuf', 'setsid', 'nohup', 'prlimit', 'faketime',
|
|
2745
|
+
]);
|
|
2746
|
+
|
|
2690
2747
|
function indirectCommitInvocation(words, initialCwd) {
|
|
2691
2748
|
if (!words.length) return false;
|
|
2692
2749
|
const executable = shellExecutable(words[0]);
|
package/src/scan-target.mjs
CHANGED
|
@@ -215,21 +215,39 @@ function scanCommit(repo, options) {
|
|
|
215
215
|
if (floor && !isVersionedStrict(rawPolicy)) {
|
|
216
216
|
accumulator.add([protectedPolicyFinding(rawPolicy, policy, snapshot)]);
|
|
217
217
|
}
|
|
218
|
-
|
|
218
|
+
// A commit is judged by what it changes, not by the whole tree it inherits.
|
|
219
|
+
// Scanning snapshot.entries (a full `ls-tree`) re-applied path rules to
|
|
220
|
+
// every file the commit merely carried forward from its parent, so a path
|
|
221
|
+
// allowed into history once (under an override that was later removed, or
|
|
222
|
+
// before the guard existed) blocked every later commit on the branch. The
|
|
223
|
+
// change set already drives content scanning; routing path rules through it
|
|
224
|
+
// too means a newly added `.env` still fires while an inherited one stays
|
|
225
|
+
// silent, matching how scanRange has always judged history.
|
|
226
|
+
scanEntryGroup(repo, loaded.engine, snapshot.changes, policy, accumulator, {
|
|
219
227
|
reviewPathEntries: snapshot.changes,
|
|
220
228
|
contentEntries: snapshot.changes,
|
|
221
229
|
allowMissingPolicy: acknowledged && !rawPolicy.policy_object_id,
|
|
222
230
|
transition: snapshot.commit,
|
|
223
231
|
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
232
|
+
// The message is the author's words, so it is scanned for attribution and
|
|
233
|
+
// markers only when the commit was written here. Fetched history (a PR
|
|
234
|
+
// checked out for review, a branch pulled from a remote) carries other
|
|
235
|
+
// people's commit text that a local developer cannot edit, so flagging it
|
|
236
|
+
// only blocks the review. cmdRefcheck passes messageScope='changes-only'
|
|
237
|
+
// for commits that were imported rather than authored; a direct `aimhooman
|
|
238
|
+
// check --commit` still defaults to scanning the message.
|
|
239
|
+
const scanMessageText = options.messageScope !== 'changes-only';
|
|
240
|
+
if (scanMessageText) {
|
|
241
|
+
accumulator.add(loaded.engine.checkMessage(snapshot.message)
|
|
242
|
+
.map((finding) => decorate(finding, snapshot, policy)));
|
|
243
|
+
accumulator.addSkipped(loaded.engine.takeSkipped());
|
|
244
|
+
}
|
|
227
245
|
return result({
|
|
228
246
|
target: `commit:${snapshot.commit}`,
|
|
229
247
|
policy,
|
|
230
248
|
accumulator,
|
|
231
249
|
diagnostics,
|
|
232
|
-
messageScanned:
|
|
250
|
+
messageScanned: scanMessageText,
|
|
233
251
|
commit: snapshot.commit,
|
|
234
252
|
});
|
|
235
253
|
}
|