@lifeaitools/rdc-skills 0.9.34 → 0.9.35
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/plugin.json +1 -1
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/hooks/rdc-invocation-marker.js +143 -0
- package/hooks/rdc-output-contract-gate.js +85 -0
- package/package.json +2 -2
- package/scripts/install-rdc-skills.js +7 -0
- package/scripts/install.ps1 +15 -0
- package/scripts/self-test.mjs +79 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v0.9.35 — RDC output-contract hook enforcement
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **RDC slash-command marker hook.** `rdc-invocation-marker.js` marks direct `/rdc:*` skill invocations through `UserPromptExpansion`, with a `UserPromptSubmit` fallback for older runtimes, and injects the output/engineering contract reminder.
|
|
15
|
+
- **RDC Stop gate.** `rdc-output-contract-gate.js` blocks completion for active RDC invocations until the final assistant message includes both a checklist row and a verdict line.
|
|
16
|
+
- **Installer wiring and self-test coverage.** The Node and PowerShell installers now wire the RDC marker/gate hooks, and `self-test` fails when required hook files or installer references are missing.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
10
20
|
## 0.6.1 — Tier 2 stability + rdc:watch session watcher
|
|
11
21
|
|
|
12
22
|
### Added
|
package/README.md
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* UserPromptExpansion/UserPromptSubmit hook — mark active rdc:* invocations.
|
|
4
|
+
*
|
|
5
|
+
* This does not enforce compliance. It primes the turn with the RDC contract
|
|
6
|
+
* and leaves a session marker for rdc-output-contract-gate.js to enforce at
|
|
7
|
+
* Stop time.
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const hookLog = require('./hook-logger');
|
|
15
|
+
|
|
16
|
+
const RDC_COMMANDS = new Set([
|
|
17
|
+
'build',
|
|
18
|
+
'co-develop',
|
|
19
|
+
'collab',
|
|
20
|
+
'deploy',
|
|
21
|
+
'design',
|
|
22
|
+
'fixit',
|
|
23
|
+
'fs-mcp',
|
|
24
|
+
'handoff',
|
|
25
|
+
'help',
|
|
26
|
+
'overnight',
|
|
27
|
+
'plan',
|
|
28
|
+
'preplan',
|
|
29
|
+
'prototype',
|
|
30
|
+
'release',
|
|
31
|
+
'report',
|
|
32
|
+
'review',
|
|
33
|
+
'self-test',
|
|
34
|
+
'status',
|
|
35
|
+
'terminal-config',
|
|
36
|
+
'watch',
|
|
37
|
+
'workitems',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
function readStdin() {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
let input = '';
|
|
43
|
+
process.stdin.setEncoding('utf8');
|
|
44
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
45
|
+
process.stdin.on('end', () => resolve(input));
|
|
46
|
+
process.stdin.resume();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function markerDir() {
|
|
51
|
+
return path.join(os.homedir(), '.claude', 'rdc-active');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function markerPath(sessionId) {
|
|
55
|
+
const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
56
|
+
return path.join(markerDir(), `${safe}.json`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeCommandName(value) {
|
|
60
|
+
return String(value || '')
|
|
61
|
+
.trim()
|
|
62
|
+
.replace(/^\/+/, '')
|
|
63
|
+
.replace(/^rdc[:-]/i, '')
|
|
64
|
+
.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function detectRdc(raw) {
|
|
68
|
+
const event = raw.hook_event_name || '';
|
|
69
|
+
if (event === 'UserPromptExpansion') {
|
|
70
|
+
if (raw.command_source && raw.command_source !== 'plugin') return null;
|
|
71
|
+
const command = normalizeCommandName(raw.command_name);
|
|
72
|
+
if (command === 'help' && !/^\/rdc[:-]help\b/i.test(String(raw.prompt || ''))) return null;
|
|
73
|
+
if (RDC_COMMANDS.has(command)) return command;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (event === 'UserPromptSubmit') {
|
|
78
|
+
const prompt = String(raw.prompt || '').trim();
|
|
79
|
+
const m = prompt.match(/^\/(?:rdc[:-])?([a-z][a-z0-9-]*)\b/i);
|
|
80
|
+
if (!m) return null;
|
|
81
|
+
const command = normalizeCommandName(m[1]);
|
|
82
|
+
if (RDC_COMMANDS.has(command)) return command;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function writeMarker(raw, command) {
|
|
89
|
+
fs.mkdirSync(markerDir(), { recursive: true });
|
|
90
|
+
const marker = {
|
|
91
|
+
session_id: raw.session_id || null,
|
|
92
|
+
command,
|
|
93
|
+
command_name: raw.command_name || null,
|
|
94
|
+
command_args: raw.command_args || null,
|
|
95
|
+
prompt: raw.prompt || null,
|
|
96
|
+
cwd: raw.cwd || null,
|
|
97
|
+
transcript_path: raw.transcript_path || null,
|
|
98
|
+
started_at: new Date().toISOString(),
|
|
99
|
+
hook_event_name: raw.hook_event_name || null,
|
|
100
|
+
};
|
|
101
|
+
fs.writeFileSync(markerPath(raw.session_id), JSON.stringify(marker, null, 2));
|
|
102
|
+
return marker;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function outputContext(eventName, command) {
|
|
106
|
+
const additionalContext = [
|
|
107
|
+
`RDC CONTRACT ACTIVE for /${command}.`,
|
|
108
|
+
'Before responding, follow the project-local guide contracts:',
|
|
109
|
+
'- .rdc/guides/output-contract.md: show one checklist for this invocation, update it as work progresses, and end with the required verdict line.',
|
|
110
|
+
'- .rdc/guides/engineering-behavior.md: assumptions, scope, evidence, deviations, blockers, and verification must be explicit.',
|
|
111
|
+
'Runtime enforcement is on emitted artifacts: the final assistant message must contain at least one checklist row and a verdict line.',
|
|
112
|
+
].join('\n');
|
|
113
|
+
|
|
114
|
+
process.stdout.write(JSON.stringify({
|
|
115
|
+
hookSpecificOutput: {
|
|
116
|
+
hookEventName: eventName,
|
|
117
|
+
additionalContext,
|
|
118
|
+
},
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function main() {
|
|
123
|
+
let raw;
|
|
124
|
+
try { raw = JSON.parse(await readStdin()); } catch { process.exit(0); }
|
|
125
|
+
const command = detectRdc(raw);
|
|
126
|
+
if (!command) process.exit(0);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const marker = writeMarker(raw, command);
|
|
130
|
+
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', 'marked', {
|
|
131
|
+
command,
|
|
132
|
+
session_id: marker.session_id,
|
|
133
|
+
});
|
|
134
|
+
outputContext(raw.hook_event_name || 'UserPromptExpansion', command);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', 'error', {
|
|
137
|
+
command,
|
|
138
|
+
error: e.message,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
main().catch(() => process.exit(0));
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stop hook — enforce visible RDC output contract artifacts.
|
|
4
|
+
*
|
|
5
|
+
* Triggered only when rdc-invocation-marker.js has marked the session. It checks
|
|
6
|
+
* positive output patterns only: at least one checklist row and one verdict
|
|
7
|
+
* line. It intentionally does not police forbidden phrases.
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const hookLog = require('./hook-logger');
|
|
15
|
+
|
|
16
|
+
function readStdin() {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
let input = '';
|
|
19
|
+
process.stdin.setEncoding('utf8');
|
|
20
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
21
|
+
process.stdin.on('end', () => resolve(input));
|
|
22
|
+
process.stdin.resume();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function markerPath(sessionId) {
|
|
27
|
+
const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
28
|
+
return path.join(os.homedir(), '.claude', 'rdc-active', `${safe}.json`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readMarker(sessionId) {
|
|
32
|
+
const p = markerPath(sessionId);
|
|
33
|
+
if (!fs.existsSync(p)) return null;
|
|
34
|
+
try {
|
|
35
|
+
return { path: p, data: JSON.parse(fs.readFileSync(p, 'utf8')) };
|
|
36
|
+
} catch {
|
|
37
|
+
return { path: p, data: { command: 'unknown' } };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hasChecklist(text) {
|
|
42
|
+
return /(?:^|\n)\s*(?:[-*]\s*)?\[(?: |x|X|~|!|-)\]\s+\S/m.test(text || '');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function hasVerdict(text) {
|
|
46
|
+
return /(?:^|\n)\s*(?:✅|⚠️|❌)\s+\S/m.test(text || '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function block(reason, details = {}) {
|
|
50
|
+
hookLog('rdc-output-contract-gate', 'Stop', 'block', details);
|
|
51
|
+
process.stdout.write(JSON.stringify({ decision: 'block', reason }));
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pass(marker) {
|
|
56
|
+
try { fs.unlinkSync(marker.path); } catch {}
|
|
57
|
+
hookLog('rdc-output-contract-gate', 'Stop', 'pass', {
|
|
58
|
+
command: marker.data.command || null,
|
|
59
|
+
});
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function main() {
|
|
64
|
+
let raw;
|
|
65
|
+
try { raw = JSON.parse(await readStdin()); } catch { process.exit(0); }
|
|
66
|
+
const marker = readMarker(raw.session_id);
|
|
67
|
+
if (!marker) process.exit(0);
|
|
68
|
+
|
|
69
|
+
const message = String(raw.last_assistant_message || '');
|
|
70
|
+
const checklist = hasChecklist(message);
|
|
71
|
+
const verdict = hasVerdict(message);
|
|
72
|
+
if (checklist && verdict) pass(marker);
|
|
73
|
+
|
|
74
|
+
const command = marker.data.command || 'rdc';
|
|
75
|
+
const missing = [];
|
|
76
|
+
if (!checklist) missing.push('a visible checklist row like `[ ] Step` or `[x] Step`');
|
|
77
|
+
if (!verdict) missing.push('a final verdict line beginning with ✅, ⚠️, or ❌');
|
|
78
|
+
|
|
79
|
+
block(
|
|
80
|
+
`RDC output contract incomplete for /${command}: missing ${missing.join(' and ')}. Continue the response by rendering the RDC checklist and verdict required by .rdc/guides/output-contract.md. Do not restart the task; correct the visible output contract.`,
|
|
81
|
+
{ command, checklist, verdict, stop_hook_active: raw.stop_hook_active === true },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main().catch((e) => block(`RDC output contract gate crashed: ${e.message}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifeaitools/rdc-skills",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.35",
|
|
4
4
|
"description": "RDC typed-agent dispatch skill suite for Claude Code - plan, build, review, overnight builds",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "plugin",
|
|
20
20
|
"skills": "skills/",
|
|
21
21
|
"guides": "guides/",
|
|
22
|
-
"version": "0.9.
|
|
22
|
+
"version": "0.9.35",
|
|
23
23
|
"commands": "commands/"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
@@ -557,6 +557,12 @@ function buildHooksConfig(hooksDir) {
|
|
|
557
557
|
return entry;
|
|
558
558
|
};
|
|
559
559
|
return {
|
|
560
|
+
UserPromptExpansion: [{ hooks: [
|
|
561
|
+
cmd('rdc-invocation-marker.js', 'Marking RDC slash command...'),
|
|
562
|
+
]}],
|
|
563
|
+
UserPromptSubmit: [{ hooks: [
|
|
564
|
+
cmd('rdc-invocation-marker.js', 'Marking RDC prompt...'),
|
|
565
|
+
]}],
|
|
560
566
|
SessionStart: [{ hooks: [
|
|
561
567
|
cmd('check-cwd.js'),
|
|
562
568
|
cmd('check-stale-work-items.js', 'Checking for stale work items...'),
|
|
@@ -582,6 +588,7 @@ function buildHooksConfig(hooksDir) {
|
|
|
582
588
|
]}],
|
|
583
589
|
Stop: [{ hooks: [
|
|
584
590
|
cmd('rate-limit-retry.js', 'Checking for rate limits...'),
|
|
591
|
+
cmd('rdc-output-contract-gate.js', 'Checking RDC output contract...'),
|
|
585
592
|
cmd('post-work-check.js', 'Checking for undocumented work...'),
|
|
586
593
|
cmd('no-stop-open-epics.js', 'Checking for open epics...'),
|
|
587
594
|
]}],
|
package/scripts/install.ps1
CHANGED
|
@@ -82,6 +82,20 @@ if ($SkipHooks) {
|
|
|
82
82
|
$hooksBase = $hooksDir.Replace("\", "/")
|
|
83
83
|
|
|
84
84
|
$hooksConfig = [PSCustomObject]@{
|
|
85
|
+
UserPromptExpansion = @(
|
|
86
|
+
[PSCustomObject]@{
|
|
87
|
+
hooks = @(
|
|
88
|
+
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/rdc-invocation-marker.js`""; statusMessage = "Marking RDC slash command..." }
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
UserPromptSubmit = @(
|
|
93
|
+
[PSCustomObject]@{
|
|
94
|
+
hooks = @(
|
|
95
|
+
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/rdc-invocation-marker.js`""; statusMessage = "Marking RDC prompt..." }
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
)
|
|
85
99
|
SessionStart = @(
|
|
86
100
|
[PSCustomObject]@{
|
|
87
101
|
hooks = @(
|
|
@@ -130,6 +144,7 @@ if ($SkipHooks) {
|
|
|
130
144
|
[PSCustomObject]@{
|
|
131
145
|
hooks = @(
|
|
132
146
|
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/rate-limit-retry.js`""; statusMessage = "Checking for rate limits..." },
|
|
147
|
+
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/rdc-output-contract-gate.js`""; statusMessage = "Checking RDC output contract..." },
|
|
133
148
|
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/post-work-check.js`""; statusMessage = "Checking for undocumented work..." },
|
|
134
149
|
[PSCustomObject]@{ type = "command"; command = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$hooksBase/run-hidden-hook.ps1`" `"$hooksBase/no-stop-open-epics.js`""; statusMessage = "Checking for open epics..." }
|
|
135
150
|
)
|
package/scripts/self-test.mjs
CHANGED
|
@@ -699,6 +699,7 @@ function auditOrphanHooks(results) {
|
|
|
699
699
|
})
|
|
700
700
|
.map((f) => join(SKILLS_DIR, f, "SKILL.md")),
|
|
701
701
|
join(REPO_ROOT, ".claude", "settings.json"),
|
|
702
|
+
join(REPO_ROOT, "scripts", "install-rdc-skills.js"),
|
|
702
703
|
PLUGIN_MANIFEST,
|
|
703
704
|
];
|
|
704
705
|
for (const src of sources) {
|
|
@@ -729,6 +730,78 @@ function auditOrphanHooks(results) {
|
|
|
729
730
|
return findings;
|
|
730
731
|
}
|
|
731
732
|
|
|
733
|
+
function auditRequiredHookWiring() {
|
|
734
|
+
const findings = [];
|
|
735
|
+
const required = [
|
|
736
|
+
{
|
|
737
|
+
file: join(REPO_ROOT, "scripts", "install-rdc-skills.js"),
|
|
738
|
+
label: "scripts/install-rdc-skills.js",
|
|
739
|
+
tokens: [
|
|
740
|
+
"UserPromptExpansion",
|
|
741
|
+
"UserPromptSubmit",
|
|
742
|
+
"Stop",
|
|
743
|
+
"rdc-invocation-marker.js",
|
|
744
|
+
"rdc-output-contract-gate.js",
|
|
745
|
+
],
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
file: join(REPO_ROOT, "scripts", "install.ps1"),
|
|
749
|
+
label: "scripts/install.ps1",
|
|
750
|
+
tokens: [
|
|
751
|
+
"UserPromptExpansion",
|
|
752
|
+
"UserPromptSubmit",
|
|
753
|
+
"Stop",
|
|
754
|
+
"rdc-invocation-marker.js",
|
|
755
|
+
"rdc-output-contract-gate.js",
|
|
756
|
+
],
|
|
757
|
+
},
|
|
758
|
+
];
|
|
759
|
+
|
|
760
|
+
for (const hookFile of [
|
|
761
|
+
"rdc-invocation-marker.js",
|
|
762
|
+
"rdc-output-contract-gate.js",
|
|
763
|
+
]) {
|
|
764
|
+
if (!existsSync(join(HOOKS_DIR, hookFile))) {
|
|
765
|
+
findings.push({
|
|
766
|
+
level: "error",
|
|
767
|
+
code: "required-hook-missing",
|
|
768
|
+
message: `hooks/${hookFile} is required for RDC output-contract enforcement`,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
for (const target of required) {
|
|
774
|
+
if (!existsSync(target.file)) {
|
|
775
|
+
findings.push({
|
|
776
|
+
level: "error",
|
|
777
|
+
code: "hook-installer-missing",
|
|
778
|
+
message: `${target.label} not found for RDC hook wiring audit`,
|
|
779
|
+
});
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
let text = "";
|
|
783
|
+
try { text = readFileSync(target.file, "utf8"); } catch (e) {
|
|
784
|
+
findings.push({
|
|
785
|
+
level: "error",
|
|
786
|
+
code: "hook-installer-unreadable",
|
|
787
|
+
message: `${target.label} unreadable: ${e.message}`,
|
|
788
|
+
});
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
for (const token of target.tokens) {
|
|
792
|
+
if (!text.includes(token)) {
|
|
793
|
+
findings.push({
|
|
794
|
+
level: "error",
|
|
795
|
+
code: "required-hook-wiring-missing",
|
|
796
|
+
message: `${target.label} must reference ${token}`,
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return findings;
|
|
803
|
+
}
|
|
804
|
+
|
|
732
805
|
// ─── Tier 2 behavioral runner ──────────────────────────────────────────────
|
|
733
806
|
|
|
734
807
|
async function runPool(items, concurrency, worker) {
|
|
@@ -1143,14 +1216,16 @@ function main() {
|
|
|
1143
1216
|
// Cross-skill checks — deferred until all audits complete
|
|
1144
1217
|
let duplicateFindings = [];
|
|
1145
1218
|
let orphanHookFindings = [];
|
|
1219
|
+
let requiredHookFindings = [];
|
|
1146
1220
|
if (!ONLY_SKILL) {
|
|
1147
1221
|
duplicateFindings = auditDuplicates(results);
|
|
1148
1222
|
orphanHookFindings = auditOrphanHooks(results);
|
|
1223
|
+
requiredHookFindings = auditRequiredHookWiring();
|
|
1149
1224
|
}
|
|
1150
1225
|
|
|
1151
|
-
if (duplicateFindings.length + orphanHookFindings.length > 0) {
|
|
1226
|
+
if (duplicateFindings.length + orphanHookFindings.length + requiredHookFindings.length > 0) {
|
|
1152
1227
|
console.log("\nglobal findings\n");
|
|
1153
|
-
for (const f of [...duplicateFindings, ...orphanHookFindings]) {
|
|
1228
|
+
for (const f of [...duplicateFindings, ...orphanHookFindings, ...requiredHookFindings]) {
|
|
1154
1229
|
console.log(` [${f.level}] ${f.code}: ${f.message}`);
|
|
1155
1230
|
}
|
|
1156
1231
|
}
|
|
@@ -1166,12 +1241,14 @@ function main() {
|
|
|
1166
1241
|
...manifestAudit.findings.filter((f) => f.level === "error"),
|
|
1167
1242
|
...duplicateFindings.filter((f) => f.level === "error"),
|
|
1168
1243
|
...orphanHookFindings.filter((f) => f.level === "error"),
|
|
1244
|
+
...requiredHookFindings.filter((f) => f.level === "error"),
|
|
1169
1245
|
...(guideValidatorResult ? guideValidatorResult.errors : []),
|
|
1170
1246
|
];
|
|
1171
1247
|
const globalWarnings = [
|
|
1172
1248
|
...manifestAudit.findings.filter((f) => f.level === "warn"),
|
|
1173
1249
|
...duplicateFindings.filter((f) => f.level === "warn"),
|
|
1174
1250
|
...orphanHookFindings.filter((f) => f.level === "warn"),
|
|
1251
|
+
...requiredHookFindings.filter((f) => f.level === "warn"),
|
|
1175
1252
|
...(guideValidatorResult ? guideValidatorResult.warnings : []),
|
|
1176
1253
|
];
|
|
1177
1254
|
|