@ijfw/install 1.3.2 → 1.4.1

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/dist/ijfw.js CHANGED
@@ -686,6 +686,996 @@ var init_gitleaks = __esm({
686
686
  }
687
687
  });
688
688
 
689
+ // ../mcp-server/src/gate-result-schema.js
690
+ function makeGateId(gate) {
691
+ if (typeof gate !== "string" || !GATE_NAME_PATTERN.test(gate)) {
692
+ throw new TypeError(
693
+ `makeGateId: invalid gate name "${gate}" \u2014 must match ${GATE_NAME_PATTERN}`
694
+ );
695
+ }
696
+ const safe = gate.replace(/:/g, "-");
697
+ const ts = Date.now();
698
+ const rand4 = Math.floor(Math.random() * 65536).toString(16).padStart(4, "0");
699
+ return `${safe}-${ts}-${rand4}`;
700
+ }
701
+ function isString(v2) {
702
+ return typeof v2 === "string";
703
+ }
704
+ function isNonNullObject(v2) {
705
+ return v2 !== null && typeof v2 === "object" && !Array.isArray(v2);
706
+ }
707
+ function validateGateResult(obj) {
708
+ const errors = [];
709
+ if (!isNonNullObject(obj)) {
710
+ return { valid: false, errors: ["root: must be an object"] };
711
+ }
712
+ if (obj.schema_version !== SCHEMA_VERSION) {
713
+ errors.push(
714
+ `schema_version: must equal "${SCHEMA_VERSION}", got ${JSON.stringify(obj.schema_version)}`
715
+ );
716
+ }
717
+ if (!isString(obj.gate)) {
718
+ errors.push("gate: must be a string");
719
+ } else if (!GATE_NAME_PATTERN.test(obj.gate)) {
720
+ errors.push(
721
+ `gate: "${obj.gate}" does not match ${GATE_NAME_PATTERN}`
722
+ );
723
+ }
724
+ if (!VALID_STATUSES.includes(obj.status)) {
725
+ errors.push(
726
+ `status: must be one of ${VALID_STATUSES.join("|")}, got ${JSON.stringify(obj.status)}`
727
+ );
728
+ }
729
+ if (!VALID_PROJECT_TYPES.includes(obj.project_type)) {
730
+ errors.push(
731
+ `project_type: must be one of ${VALID_PROJECT_TYPES.join("|")}, got ${JSON.stringify(obj.project_type)}`
732
+ );
733
+ }
734
+ if (!Array.isArray(obj.lenses)) {
735
+ errors.push("lenses: must be an array (empty for single-model gates)");
736
+ } else {
737
+ obj.lenses.forEach((lens, i) => {
738
+ if (!isNonNullObject(lens)) {
739
+ errors.push(`lenses[${i}]: must be an object`);
740
+ return;
741
+ }
742
+ if (!isString(lens.model)) errors.push(`lenses[${i}].model: must be a string`);
743
+ if (!VALID_STATUSES.includes(lens.verdict)) {
744
+ errors.push(
745
+ `lenses[${i}].verdict: must be one of ${VALID_STATUSES.join("|")}`
746
+ );
747
+ }
748
+ if (typeof lens.confidence !== "number" || lens.confidence < 0 || lens.confidence > 1) {
749
+ errors.push(`lenses[${i}].confidence: must be number in [0,1]`);
750
+ }
751
+ if (!isString(lens.summary)) errors.push(`lenses[${i}].summary: must be a string`);
752
+ });
753
+ }
754
+ if (!Array.isArray(obj.affected_artifacts)) {
755
+ errors.push("affected_artifacts: must be an array");
756
+ } else {
757
+ obj.affected_artifacts.forEach((a, i) => {
758
+ if (!isNonNullObject(a)) {
759
+ errors.push(`affected_artifacts[${i}]: must be an object`);
760
+ return;
761
+ }
762
+ if (!VALID_ARTIFACT_TYPES.includes(a.type)) {
763
+ errors.push(
764
+ `affected_artifacts[${i}].type: must be one of ${VALID_ARTIFACT_TYPES.join("|")}`
765
+ );
766
+ }
767
+ if (!isString(a.ref)) errors.push(`affected_artifacts[${i}].ref: must be a string`);
768
+ if (!isString(a.role)) errors.push(`affected_artifacts[${i}].role: must be a string`);
769
+ });
770
+ }
771
+ if (!isNonNullObject(obj.accounting)) {
772
+ errors.push("accounting: must be an object");
773
+ } else {
774
+ const a = obj.accounting;
775
+ if (typeof a.duration_ms !== "number" || a.duration_ms < 0) {
776
+ errors.push("accounting.duration_ms: must be a non-negative number");
777
+ }
778
+ if (typeof a.lenses_invoked !== "number" || a.lenses_invoked < 0 || !Number.isInteger(a.lenses_invoked)) {
779
+ errors.push("accounting.lenses_invoked: must be a non-negative integer");
780
+ }
781
+ if (a.cost_usd !== null && (typeof a.cost_usd !== "number" || a.cost_usd < 0)) {
782
+ errors.push("accounting.cost_usd: must be null or non-negative number");
783
+ }
784
+ }
785
+ if (!Array.isArray(obj.remediation)) {
786
+ errors.push("remediation: must be an array (may be empty)");
787
+ } else {
788
+ obj.remediation.forEach((r, i) => {
789
+ if (!isNonNullObject(r)) {
790
+ errors.push(`remediation[${i}]: must be an object`);
791
+ return;
792
+ }
793
+ if (!isString(r.action)) errors.push(`remediation[${i}].action: must be a string`);
794
+ if (!isString(r.target)) errors.push(`remediation[${i}].target: must be a string`);
795
+ if (!isString(r.agent_recommended)) {
796
+ errors.push(`remediation[${i}].agent_recommended: must be a string`);
797
+ }
798
+ if (typeof r.confidence !== "number" || r.confidence < 0 || r.confidence > 1) {
799
+ errors.push(`remediation[${i}].confidence: must be number in [0,1]`);
800
+ }
801
+ });
802
+ }
803
+ if (obj.receipts_ref !== null && !isString(obj.receipts_ref)) {
804
+ errors.push("receipts_ref: must be a string or null");
805
+ }
806
+ if (obj.supersedes !== null && !isString(obj.supersedes)) {
807
+ errors.push("supersedes: must be a string or null");
808
+ }
809
+ if (!isString(obj.gate_id) || obj.gate_id.length === 0) {
810
+ errors.push("gate_id: must be a non-empty string");
811
+ } else if (isString(obj.gate)) {
812
+ const safeGate = obj.gate.replace(/:/g, "-");
813
+ if (!obj.gate_id.startsWith(safeGate + "-")) {
814
+ errors.push(
815
+ `gate_id: expected to start with "${safeGate}-" (colon-collapsed gate name)`
816
+ );
817
+ }
818
+ }
819
+ if (!isString(obj.emitted_at) || !ISO8601_PATTERN.test(obj.emitted_at)) {
820
+ errors.push("emitted_at: must be ISO-8601 string");
821
+ }
822
+ return { valid: errors.length === 0, errors };
823
+ }
824
+ function formatGateResult(obj) {
825
+ const json = JSON.stringify(obj, null, 2);
826
+ return "```gate-result\n" + json + "\n```";
827
+ }
828
+ var SCHEMA_VERSION, GATE_NAME_PATTERN, VALID_STATUSES, VALID_PROJECT_TYPES, VALID_ARTIFACT_TYPES, ISO8601_PATTERN;
829
+ var init_gate_result_schema = __esm({
830
+ "../mcp-server/src/gate-result-schema.js"() {
831
+ SCHEMA_VERSION = "1.0";
832
+ GATE_NAME_PATTERN = /^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)?$/;
833
+ VALID_STATUSES = Object.freeze([
834
+ "PASS",
835
+ "CONDITIONAL",
836
+ "WARN",
837
+ "FLAG",
838
+ "FAIL"
839
+ ]);
840
+ VALID_PROJECT_TYPES = Object.freeze([
841
+ "software",
842
+ "book",
843
+ "content",
844
+ "business",
845
+ "design",
846
+ "mixed",
847
+ "unknown"
848
+ ]);
849
+ VALID_ARTIFACT_TYPES = Object.freeze([
850
+ "file",
851
+ "chapter",
852
+ "section",
853
+ "asset",
854
+ "persona",
855
+ "decision",
856
+ "component"
857
+ ]);
858
+ ISO8601_PATTERN = // eslint-disable-next-line security/detect-unsafe-regex -- fixed-length anchored ISO 8601 shape; optional fractional + tz are non-overlapping
859
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})$/;
860
+ }
861
+ });
862
+
863
+ // ../mcp-server/src/scan-resume.js
864
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, unlinkSync, copyFileSync } from "fs";
865
+ import { join as join4 } from "path";
866
+ function statePath(projectRoot) {
867
+ return join4(String(projectRoot), ".ijfw", STATE_FILE);
868
+ }
869
+ function loadScanState(projectRoot) {
870
+ const path = statePath(projectRoot);
871
+ if (!existsSync(path)) return null;
872
+ try {
873
+ const raw = readFileSync2(path, "utf8");
874
+ const parsed = JSON.parse(raw);
875
+ if (parsed && typeof parsed === "object") return parsed;
876
+ } catch {
877
+ }
878
+ return null;
879
+ }
880
+ function writeScanState(projectRoot, state) {
881
+ const dir = join4(String(projectRoot), ".ijfw");
882
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
883
+ const finalPath = statePath(projectRoot);
884
+ const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
885
+ const safe = {
886
+ scan_id: String(state.scan_id || ""),
887
+ started_at: String(state.started_at || (/* @__PURE__ */ new Date()).toISOString()),
888
+ last_path_walked: String(state.last_path_walked || ""),
889
+ files_scanned: Number.isFinite(state.files_scanned) ? state.files_scanned : 0,
890
+ total_estimate: Number.isFinite(state.total_estimate) ? state.total_estimate : 0,
891
+ attempts: Number.isFinite(state.attempts) ? state.attempts : 1,
892
+ incomplete: state.incomplete !== false,
893
+ session_id: state.session_id || null
894
+ };
895
+ if (state.partial && typeof state.partial === "object") {
896
+ safe.partial = state.partial;
897
+ }
898
+ writeFileSync2(tmpPath, JSON.stringify(safe, null, 2) + "\n", "utf8");
899
+ try {
900
+ renameSync(tmpPath, finalPath);
901
+ } catch (err) {
902
+ if (!err || err.code !== "EXDEV") throw err;
903
+ try {
904
+ copyFileSync(tmpPath, finalPath);
905
+ } finally {
906
+ try {
907
+ unlinkSync(tmpPath);
908
+ } catch {
909
+ }
910
+ }
911
+ }
912
+ return finalPath;
913
+ }
914
+ function lockPath(projectRoot) {
915
+ return join4(String(projectRoot), ".ijfw", LOCK_FILE);
916
+ }
917
+ function isPidAlive(pid) {
918
+ if (!Number.isFinite(pid) || pid <= 0) return false;
919
+ try {
920
+ process.kill(pid, 0);
921
+ return true;
922
+ } catch (err) {
923
+ if (err && err.code === "EPERM") return true;
924
+ return false;
925
+ }
926
+ }
927
+ function reclaimIfStale(lp) {
928
+ if (!existsSync(lp)) return;
929
+ let raw;
930
+ try {
931
+ raw = readFileSync2(lp, "utf8");
932
+ } catch {
933
+ return;
934
+ }
935
+ const lines = String(raw).split(/\r?\n/);
936
+ const pid = Number(lines[0]);
937
+ const ts = Number(lines[1]);
938
+ const ageOk = Number.isFinite(ts) && Date.now() - ts <= LOCK_STALE_MS;
939
+ if (isPidAlive(pid) && ageOk) return;
940
+ try {
941
+ unlinkSync(lp);
942
+ } catch {
943
+ }
944
+ }
945
+ function acquireScanLock(projectRoot) {
946
+ const dir = join4(String(projectRoot), ".ijfw");
947
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
948
+ const lp = lockPath(projectRoot);
949
+ reclaimIfStale(lp);
950
+ const payload = String(process.pid) + "\n" + String(Date.now()) + "\n";
951
+ try {
952
+ writeFileSync2(lp, payload, { encoding: "utf8", flag: "wx" });
953
+ } catch (err) {
954
+ if (err && err.code === "EEXIST") return null;
955
+ throw err;
956
+ }
957
+ let released = false;
958
+ return {
959
+ released: () => {
960
+ if (released) return;
961
+ released = true;
962
+ try {
963
+ unlinkSync(lp);
964
+ } catch {
965
+ }
966
+ }
967
+ };
968
+ }
969
+ function shouldResume(state) {
970
+ if (!state || typeof state !== "object") return false;
971
+ if (state.incomplete !== true) return false;
972
+ if (!state.started_at || typeof state.started_at !== "string") return false;
973
+ const startedMs = Date.parse(state.started_at);
974
+ if (!Number.isFinite(startedMs)) return false;
975
+ const ageMs = Date.now() - startedMs;
976
+ if (ageMs > STALENESS_MS) return false;
977
+ const attempts = Number.isFinite(state.attempts) ? state.attempts : 0;
978
+ if (attempts >= ATTEMPT_CAP) return false;
979
+ return true;
980
+ }
981
+ function clearScanState(projectRoot) {
982
+ const path = statePath(projectRoot);
983
+ if (existsSync(path)) {
984
+ try {
985
+ unlinkSync(path);
986
+ } catch {
987
+ }
988
+ }
989
+ }
990
+ var STATE_FILE, LOCK_FILE, STALENESS_MS, ATTEMPT_CAP, LOCK_STALE_MS;
991
+ var init_scan_resume = __esm({
992
+ "../mcp-server/src/scan-resume.js"() {
993
+ STATE_FILE = "scan-state.json";
994
+ LOCK_FILE = "scan-state.json.lock";
995
+ STALENESS_MS = 24 * 60 * 60 * 1e3;
996
+ ATTEMPT_CAP = 3;
997
+ LOCK_STALE_MS = 60 * 1e3;
998
+ }
999
+ });
1000
+
1001
+ // ../mcp-server/src/project-type-detector.js
1002
+ import {
1003
+ readFileSync as readFileSync3,
1004
+ writeFileSync as writeFileSync3,
1005
+ existsSync as existsSync2,
1006
+ readdirSync as readdirSync3,
1007
+ statSync as statSync3,
1008
+ renameSync as renameSync2,
1009
+ mkdirSync as mkdirSync2,
1010
+ unlinkSync as unlinkSync2,
1011
+ realpathSync,
1012
+ copyFileSync as copyFileSync2
1013
+ } from "fs";
1014
+ import { join as join5, extname, isAbsolute, resolve as pathResolve, dirname } from "path";
1015
+ import { fileURLToPath } from "url";
1016
+ import { createHash } from "crypto";
1017
+ function detect(projectRoot, options = {}) {
1018
+ const root = String(projectRoot || process.cwd());
1019
+ const c9Available = options.c9Available === false ? false : options.c9Available === true ? true : isC9AvailableSync();
1020
+ const maxFiles = Number.isFinite(options.maxFiles) && options.maxFiles > 0 ? options.maxFiles : MAX_FILES;
1021
+ const signals = [];
1022
+ const fallbackReason = c9Available ? null : "c9_unavailable";
1023
+ if (options.explicitType && DOMAINS.includes(String(options.explicitType))) {
1024
+ signals.push({ kind: "user_declaration", weight: 1, value: options.explicitType });
1025
+ return finalize({
1026
+ primary: options.explicitType,
1027
+ secondary: [],
1028
+ score: 1,
1029
+ signals,
1030
+ scanIncomplete: false,
1031
+ fallbackReason,
1032
+ treeHash: "",
1033
+ branchHash: branchHash(root)
1034
+ });
1035
+ }
1036
+ const fmAgents = readFrontmatterType(join5(root, "AGENTS.md"));
1037
+ if (fmAgents && DOMAINS.includes(fmAgents)) {
1038
+ signals.push({ kind: "agents_md_frontmatter", weight: 0.9, value: fmAgents });
1039
+ }
1040
+ const fmBrief = readFrontmatterType(join5(root, ".ijfw", "memory", "brief.md"));
1041
+ if (fmBrief && DOMAINS.includes(fmBrief)) {
1042
+ signals.push({ kind: "brief_md_frontmatter", weight: 0.8, value: fmBrief });
1043
+ }
1044
+ const timeBudgetMs = resolveTimeBudgetMs(options);
1045
+ const walk = walkProject(root, { maxFiles, maxDepth: MAX_DEPTH, options, timeBudgetMs });
1046
+ const treeHash = fileTreeHash(walk.fingerprint);
1047
+ if (walk.manifestsFound.length > 0) {
1048
+ signals.push({
1049
+ kind: "manifest",
1050
+ weight: 0.9,
1051
+ manifests: walk.manifestsFound.slice(0, 6)
1052
+ });
1053
+ }
1054
+ for (const d of walk.dirHits.book) signals.push({ kind: "dir_book", weight: 0.4, name: d });
1055
+ for (const d of walk.dirHits.content) signals.push({ kind: "dir_content", weight: 0.4, name: d });
1056
+ for (const d of walk.dirHits.business) signals.push({ kind: "dir_business", weight: 0.4, name: d });
1057
+ for (const d of walk.dirHits.design) signals.push({ kind: "dir_design", weight: 0.4, name: d });
1058
+ const totals = walk.extTotals;
1059
+ const totalClassified = Object.values(totals).reduce((a, b2) => a + b2, 0);
1060
+ if (totalClassified > 0) {
1061
+ for (const [domain, count] of Object.entries(totals)) {
1062
+ const ratio = count / totalClassified;
1063
+ if (ratio >= 0.05) {
1064
+ signals.push({
1065
+ kind: "file_extension_ratio",
1066
+ weight: 0.7,
1067
+ domain,
1068
+ ratio: Number(ratio.toFixed(3)),
1069
+ count
1070
+ });
1071
+ }
1072
+ }
1073
+ }
1074
+ for (const hit of walk.patternHits) {
1075
+ signals.push({ kind: "filename_pattern", weight: hit.weight, domain: hit.domain, name: hit.name });
1076
+ }
1077
+ const scoreboard = scoreSignals(signals);
1078
+ const ranked = rankDomains(scoreboard);
1079
+ let primary;
1080
+ let secondary = [];
1081
+ let confidence;
1082
+ if (ranked.length === 0) {
1083
+ primary = "unknown";
1084
+ confidence = 0;
1085
+ } else {
1086
+ primary = ranked[0].domain;
1087
+ confidence = ranked[0].score;
1088
+ secondary = ranked.slice(1).filter((r) => r.score >= 0.4 && r.domain !== primary).map((r) => r.domain);
1089
+ if (ranked.length >= 2 && ranked[0].score >= 0.55 && ranked[1].score >= 0.5 && ranked[1].score / ranked[0].score >= 0.75) {
1090
+ const topTwo = [ranked[0].domain, ranked[1].domain];
1091
+ secondary = topTwo;
1092
+ primary = "mixed";
1093
+ confidence = Math.min(0.85, (ranked[0].score + ranked[1].score) / 2);
1094
+ }
1095
+ }
1096
+ const highTrust = signals.some(
1097
+ (s) => s.kind === "user_declaration" || s.kind === "agents_md_frontmatter" || s.kind === "brief_md_frontmatter"
1098
+ );
1099
+ if (!c9Available && !highTrust && confidence > 0.7) confidence = 0.7;
1100
+ const scanIncomplete = walk.incomplete;
1101
+ if (scanIncomplete) {
1102
+ const lock = acquireScanLock(root);
1103
+ if (lock) {
1104
+ try {
1105
+ const prior = loadScanState(root) || {};
1106
+ writeScanState(root, {
1107
+ scan_id: prior.scan_id || newScanId(),
1108
+ started_at: prior.started_at || (/* @__PURE__ */ new Date()).toISOString(),
1109
+ last_path_walked: walk.lastPathWalked,
1110
+ files_scanned: walk.filesScanned,
1111
+ total_estimate: walk.totalEstimate,
1112
+ attempts: (prior.attempts || 0) + 1,
1113
+ incomplete: true,
1114
+ session_id: options.sessionId || null,
1115
+ partial: snapshotPartial(walk)
1116
+ });
1117
+ } catch {
1118
+ } finally {
1119
+ lock.released();
1120
+ }
1121
+ }
1122
+ } else {
1123
+ try {
1124
+ clearScanState(root);
1125
+ } catch {
1126
+ }
1127
+ }
1128
+ return finalize({
1129
+ primary,
1130
+ secondary,
1131
+ score: confidence,
1132
+ signals,
1133
+ scanIncomplete,
1134
+ fallbackReason,
1135
+ treeHash,
1136
+ branchHash: branchHash(root)
1137
+ });
1138
+ }
1139
+ function finalize({ primary, secondary, score, signals, scanIncomplete, fallbackReason, treeHash, branchHash: bh }) {
1140
+ const confidence = Number(Math.max(0, Math.min(1, score)).toFixed(3));
1141
+ const out = {
1142
+ type: primary,
1143
+ // single-label alias for hoist
1144
+ primary_type: primary,
1145
+ secondary_types: Array.isArray(secondary) ? secondary : [],
1146
+ confidence,
1147
+ scan_incomplete: !!scanIncomplete,
1148
+ detected_at: (/* @__PURE__ */ new Date()).toISOString(),
1149
+ signals,
1150
+ fallback_reason: fallbackReason,
1151
+ file_tree_hash: treeHash || "",
1152
+ branch_hash: bh || ""
1153
+ };
1154
+ return out;
1155
+ }
1156
+ function readFrontmatterType(path) {
1157
+ if (!existsSync2(path)) return null;
1158
+ let src;
1159
+ try {
1160
+ src = readFileSync3(path, "utf8");
1161
+ } catch {
1162
+ return null;
1163
+ }
1164
+ if (!src.startsWith("---\n")) return null;
1165
+ const after = src.slice(4);
1166
+ const closeIdx = after.search(/\n---\s*(?:\r?\n|$)/);
1167
+ if (closeIdx < 0) return null;
1168
+ const fm = after.slice(0, closeIdx);
1169
+ for (const ln of fm.split(/\r?\n/)) {
1170
+ const m2 = ln.match(/^type\s*:\s*(\S+)\s*$/);
1171
+ if (m2) {
1172
+ const v2 = m2[1].replace(/^["']|["']$/g, "");
1173
+ return v2;
1174
+ }
1175
+ }
1176
+ return null;
1177
+ }
1178
+ function walkProject(root, { maxFiles, maxDepth, options, timeBudgetMs }) {
1179
+ const out = {
1180
+ filesScanned: 0,
1181
+ totalEstimate: 0,
1182
+ incomplete: false,
1183
+ lastPathWalked: "",
1184
+ fingerprint: [],
1185
+ manifestsFound: [],
1186
+ dirHits: { book: [], content: [], business: [], design: [] },
1187
+ extTotals: {},
1188
+ patternHits: []
1189
+ };
1190
+ let resumeFrom = null;
1191
+ let priorState = null;
1192
+ if (options.resume !== false) {
1193
+ const state = loadScanState(root);
1194
+ if (state && shouldResume(state)) {
1195
+ resumeFrom = state.last_path_walked || null;
1196
+ priorState = state;
1197
+ }
1198
+ }
1199
+ if (priorState && priorState.partial && typeof priorState.partial === "object") {
1200
+ const p = priorState.partial;
1201
+ out.filesScanned = Number.isFinite(p.files_scanned) ? p.files_scanned : 0;
1202
+ out.totalEstimate = Number.isFinite(p.total_estimate) ? p.total_estimate : out.filesScanned;
1203
+ if (Array.isArray(p.fingerprint)) out.fingerprint = p.fingerprint.slice(0, 4096);
1204
+ if (Array.isArray(p.manifestsFound)) out.manifestsFound = p.manifestsFound.slice();
1205
+ if (p.dirHits && typeof p.dirHits === "object") {
1206
+ for (const k2 of ["book", "content", "business", "design"]) {
1207
+ if (Array.isArray(p.dirHits[k2])) out.dirHits[k2] = p.dirHits[k2].slice();
1208
+ }
1209
+ }
1210
+ if (p.extTotals && typeof p.extTotals === "object") out.extTotals = { ...p.extTotals };
1211
+ if (Array.isArray(p.patternHits)) out.patternHits = p.patternHits.slice();
1212
+ }
1213
+ let resumed = !resumeFrom;
1214
+ const visitedDirs = /* @__PURE__ */ new Set();
1215
+ try {
1216
+ visitedDirs.add(realpathSync.native(root));
1217
+ } catch {
1218
+ }
1219
+ const startedAt = Date.now();
1220
+ const budget = Number.isFinite(timeBudgetMs) && timeBudgetMs > 0 ? timeBudgetMs : DEFAULT_TIME_BUDGET_MS;
1221
+ let entriesSinceTimeCheck = 0;
1222
+ const stack = [{ path: root, depth: 0 }];
1223
+ while (stack.length > 0) {
1224
+ const { path, depth } = stack.pop();
1225
+ if (depth > maxDepth) continue;
1226
+ let entries;
1227
+ try {
1228
+ entries = readdirSync3(path, { withFileTypes: true });
1229
+ } catch {
1230
+ continue;
1231
+ }
1232
+ entries.sort((a, b2) => a.name < b2.name ? -1 : a.name > b2.name ? 1 : 0);
1233
+ for (const entry of entries) {
1234
+ const childPath = join5(path, entry.name);
1235
+ if (!resumed) {
1236
+ if (childPath === resumeFrom) resumed = true;
1237
+ continue;
1238
+ }
1239
+ out.lastPathWalked = childPath;
1240
+ entriesSinceTimeCheck += 1;
1241
+ if (entriesSinceTimeCheck >= TIME_BUDGET_CHECK_EVERY) {
1242
+ entriesSinceTimeCheck = 0;
1243
+ if (Date.now() - startedAt > budget) {
1244
+ out.incomplete = true;
1245
+ return out;
1246
+ }
1247
+ }
1248
+ if (entry.isDirectory()) {
1249
+ if (SKIP_DIRS.has(entry.name)) continue;
1250
+ try {
1251
+ const real = realpathSync.native(childPath);
1252
+ if (visitedDirs.has(real)) continue;
1253
+ visitedDirs.add(real);
1254
+ } catch {
1255
+ }
1256
+ recordDirHit(out, entry.name, depth);
1257
+ stack.push({ path: childPath, depth: depth + 1 });
1258
+ continue;
1259
+ }
1260
+ if (!entry.isFile()) continue;
1261
+ out.filesScanned += 1;
1262
+ out.totalEstimate = Math.max(out.totalEstimate, out.filesScanned);
1263
+ if (out.fingerprint.length < 4096) {
1264
+ out.fingerprint.push(childPath.slice(root.length + 1));
1265
+ }
1266
+ if (depth <= 2 && SOFTWARE_MANIFESTS.includes(entry.name)) {
1267
+ out.manifestsFound.push(entry.name);
1268
+ }
1269
+ for (const p of FILENAME_PATTERNS) {
1270
+ if (p.re.test(entry.name)) {
1271
+ out.patternHits.push({ name: entry.name, domain: p.domain, weight: p.weight });
1272
+ break;
1273
+ }
1274
+ }
1275
+ const ext = extname(entry.name).toLowerCase();
1276
+ const dom = EXT_DOMAIN[ext];
1277
+ if (dom) {
1278
+ out.extTotals[dom] = (out.extTotals[dom] || 0) + 1;
1279
+ }
1280
+ if (out.filesScanned % CHECKPOINT_EVERY === 0) {
1281
+ try {
1282
+ writeScanState(root, {
1283
+ scan_id: newScanId(),
1284
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
1285
+ last_path_walked: childPath,
1286
+ files_scanned: out.filesScanned,
1287
+ total_estimate: out.totalEstimate,
1288
+ attempts: 1,
1289
+ incomplete: true,
1290
+ partial: snapshotPartial(out)
1291
+ });
1292
+ } catch {
1293
+ }
1294
+ }
1295
+ if (out.filesScanned >= maxFiles) {
1296
+ out.incomplete = true;
1297
+ return out;
1298
+ }
1299
+ }
1300
+ }
1301
+ return out;
1302
+ }
1303
+ function snapshotPartial(out) {
1304
+ return {
1305
+ files_scanned: out.filesScanned,
1306
+ total_estimate: out.totalEstimate,
1307
+ fingerprint: out.fingerprint.slice(0, 4096),
1308
+ manifestsFound: out.manifestsFound.slice(0, 32),
1309
+ dirHits: {
1310
+ book: out.dirHits.book.slice(0, 32),
1311
+ content: out.dirHits.content.slice(0, 32),
1312
+ business: out.dirHits.business.slice(0, 32),
1313
+ design: out.dirHits.design.slice(0, 32)
1314
+ },
1315
+ extTotals: { ...out.extTotals },
1316
+ patternHits: out.patternHits.slice(0, 64)
1317
+ };
1318
+ }
1319
+ function resolveTimeBudgetMs(options) {
1320
+ if (Number.isFinite(options.timeBudgetMs) && options.timeBudgetMs > 0) {
1321
+ return options.timeBudgetMs;
1322
+ }
1323
+ const env = process.env.IJFW_DETECT_TIME_BUDGET_MS;
1324
+ if (env) {
1325
+ const n = Number(env);
1326
+ if (Number.isFinite(n) && n > 0) return n;
1327
+ }
1328
+ return DEFAULT_TIME_BUDGET_MS;
1329
+ }
1330
+ function recordDirHit(out, name12, depth) {
1331
+ if (depth > 2) return;
1332
+ const lower = name12.toLowerCase();
1333
+ if (BOOK_DIRS.includes(lower)) out.dirHits.book.push(lower);
1334
+ if (CONTENT_DIRS.includes(lower)) out.dirHits.content.push(lower);
1335
+ if (BUSINESS_DIRS.includes(lower)) out.dirHits.business.push(lower);
1336
+ if (DESIGN_DIRS.includes(lower)) out.dirHits.design.push(lower);
1337
+ }
1338
+ function scoreSignals(signals) {
1339
+ const board = {
1340
+ software: 0,
1341
+ book: 0,
1342
+ content: 0,
1343
+ business: 0,
1344
+ design: 0,
1345
+ mixed: 0,
1346
+ unknown: 0
1347
+ };
1348
+ const patternBudget = { software: 0.8, book: 0.8, content: 0.8, business: 0.8, design: 0.8, mixed: 0.8, unknown: 0.8 };
1349
+ const dirBudget = { software: 0.6, book: 0.6, content: 0.6, business: 0.6, design: 0.6, mixed: 0.6, unknown: 0.6 };
1350
+ for (const s of signals) {
1351
+ if (s.kind === "user_declaration" && s.value) board[s.value] += 1;
1352
+ else if (s.kind === "agents_md_frontmatter" && s.value) board[s.value] += 0.9;
1353
+ else if (s.kind === "brief_md_frontmatter" && s.value) board[s.value] += 0.8;
1354
+ else if (s.kind === "manifest") board.software += 0.9;
1355
+ else if (s.kind === "dir_book") {
1356
+ const add = Math.min(0.4, dirBudget.book);
1357
+ board.book += add;
1358
+ dirBudget.book -= add;
1359
+ } else if (s.kind === "dir_content") {
1360
+ const add = Math.min(0.4, dirBudget.content);
1361
+ board.content += add;
1362
+ dirBudget.content -= add;
1363
+ } else if (s.kind === "dir_business") {
1364
+ const add = Math.min(0.4, dirBudget.business);
1365
+ board.business += add;
1366
+ dirBudget.business -= add;
1367
+ } else if (s.kind === "dir_design") {
1368
+ const add = Math.min(0.4, dirBudget.design);
1369
+ board.design += add;
1370
+ dirBudget.design -= add;
1371
+ } else if (s.kind === "file_extension_ratio") {
1372
+ const m2 = s.ratio || 0;
1373
+ board[s.domain] = (board[s.domain] || 0) + 0.7 * m2;
1374
+ } else if (s.kind === "filename_pattern") {
1375
+ const add = Math.min(s.weight, patternBudget[s.domain] || 0);
1376
+ if (add > 0) {
1377
+ board[s.domain] = (board[s.domain] || 0) + add;
1378
+ patternBudget[s.domain] -= add;
1379
+ }
1380
+ }
1381
+ }
1382
+ return board;
1383
+ }
1384
+ function rankDomains(board) {
1385
+ const arr = Object.entries(board).filter(([d]) => d !== "mixed" && d !== "unknown").map(([domain, raw]) => ({ domain, raw }));
1386
+ if (arr.length === 0) return [];
1387
+ const maxRaw = arr.reduce((m2, e) => Math.max(m2, e.raw), 0);
1388
+ if (maxRaw <= 0) return [];
1389
+ for (const e of arr) {
1390
+ e.score = anchor(e.raw, maxRaw);
1391
+ }
1392
+ arr.sort((a, b2) => b2.score - a.score);
1393
+ return arr;
1394
+ }
1395
+ function anchor(raw, maxRaw) {
1396
+ if (raw <= 0) return 0;
1397
+ const top = Math.min(0.95, 0.4 + 0.55 * Math.tanh(raw));
1398
+ return Number((top * (raw / maxRaw)).toFixed(3));
1399
+ }
1400
+ function fileTreeHash(paths) {
1401
+ if (!paths || paths.length === 0) return "";
1402
+ const h = createHash("sha256");
1403
+ for (const p of paths) h.update(p + "\n");
1404
+ return h.digest("hex").slice(0, 16);
1405
+ }
1406
+ function branchHash(root) {
1407
+ try {
1408
+ const dotGit = join5(root, ".git");
1409
+ if (!existsSync2(dotGit)) return "";
1410
+ let headPath = null;
1411
+ let st;
1412
+ try {
1413
+ st = statSync3(dotGit);
1414
+ } catch {
1415
+ return "";
1416
+ }
1417
+ if (st.isDirectory()) {
1418
+ headPath = join5(dotGit, "HEAD");
1419
+ } else if (st.isFile()) {
1420
+ const ptr = readFileSync3(dotGit, "utf8");
1421
+ const m3 = ptr.match(/^gitdir:\s*(.+?)\s*$/m);
1422
+ if (!m3) return "";
1423
+ const target = m3[1];
1424
+ const gitDir = isAbsolute(target) ? target : pathResolve(root, target);
1425
+ headPath = join5(gitDir, "HEAD");
1426
+ } else {
1427
+ return "";
1428
+ }
1429
+ if (!headPath || !existsSync2(headPath)) return "";
1430
+ const head = readFileSync3(headPath, "utf8").trim();
1431
+ const m2 = head.match(/^ref:\s*(.+)$/);
1432
+ const branch = m2 ? m2[1] : head;
1433
+ return createHash("sha256").update(branch).digest("hex").slice(0, 16);
1434
+ } catch {
1435
+ }
1436
+ return "";
1437
+ }
1438
+ function isC9AvailableSync() {
1439
+ if (_c9AvailableCache !== null) return _c9AvailableCache;
1440
+ try {
1441
+ const here = fileURLToPath(import.meta.url);
1442
+ const fts5Path = join5(dirname(here), "compute", "fts5.js");
1443
+ _c9AvailableCache = existsSync2(fts5Path);
1444
+ } catch {
1445
+ _c9AvailableCache = false;
1446
+ }
1447
+ return _c9AvailableCache;
1448
+ }
1449
+ function newScanId() {
1450
+ return createHash("sha256").update(String(process.pid) + ":" + String(Date.now()) + ":" + Math.random()).digest("hex").slice(0, 12);
1451
+ }
1452
+ var DOMAINS, MAX_FILES, MAX_DEPTH, CHECKPOINT_EVERY, DEFAULT_TIME_BUDGET_MS, TIME_BUDGET_CHECK_EVERY, SKIP_DIRS, EXT_DOMAIN, SOFTWARE_MANIFESTS, BOOK_DIRS, CONTENT_DIRS, BUSINESS_DIRS, DESIGN_DIRS, FILENAME_PATTERNS, _c9AvailableCache;
1453
+ var init_project_type_detector = __esm({
1454
+ "../mcp-server/src/project-type-detector.js"() {
1455
+ init_scan_resume();
1456
+ DOMAINS = ["software", "book", "content", "business", "design", "mixed", "unknown"];
1457
+ MAX_FILES = 2e5;
1458
+ MAX_DEPTH = 12;
1459
+ CHECKPOINT_EVERY = 500;
1460
+ DEFAULT_TIME_BUDGET_MS = 5e3;
1461
+ TIME_BUDGET_CHECK_EVERY = 1e3;
1462
+ SKIP_DIRS = /* @__PURE__ */ new Set([
1463
+ ".git",
1464
+ ".hg",
1465
+ ".svn",
1466
+ ".ijfw",
1467
+ ".planning",
1468
+ ".cache",
1469
+ "node_modules",
1470
+ "dist",
1471
+ "build",
1472
+ "out",
1473
+ "target",
1474
+ ".next",
1475
+ "__pycache__",
1476
+ ".venv",
1477
+ "venv",
1478
+ "env",
1479
+ ".pytest_cache",
1480
+ ".mypy_cache",
1481
+ ".tox",
1482
+ ".gradle",
1483
+ ".idea",
1484
+ ".vscode",
1485
+ "vendor",
1486
+ "bower_components"
1487
+ ]);
1488
+ EXT_DOMAIN = {
1489
+ // software (heavy)
1490
+ ".js": "software",
1491
+ ".jsx": "software",
1492
+ ".ts": "software",
1493
+ ".tsx": "software",
1494
+ ".mjs": "software",
1495
+ ".cjs": "software",
1496
+ ".py": "software",
1497
+ ".rs": "software",
1498
+ ".go": "software",
1499
+ ".java": "software",
1500
+ ".kt": "software",
1501
+ ".scala": "software",
1502
+ ".rb": "software",
1503
+ ".php": "software",
1504
+ ".c": "software",
1505
+ ".cc": "software",
1506
+ ".cpp": "software",
1507
+ ".h": "software",
1508
+ ".hpp": "software",
1509
+ ".hh": "software",
1510
+ ".swift": "software",
1511
+ ".m": "software",
1512
+ ".mm": "software",
1513
+ ".cs": "software",
1514
+ ".fs": "software",
1515
+ ".lua": "software",
1516
+ ".dart": "software",
1517
+ ".zig": "software",
1518
+ // book / long-form prose
1519
+ ".tex": "book",
1520
+ ".bib": "book",
1521
+ ".latex": "book",
1522
+ ".epub": "book",
1523
+ ".mobi": "book",
1524
+ // content / blog / docs / marketing
1525
+ ".mdx": "content",
1526
+ ".markdown": "content",
1527
+ ".rst": "content",
1528
+ // design / assets
1529
+ ".fig": "design",
1530
+ ".sketch": "design",
1531
+ ".xd": "design",
1532
+ ".ai": "design",
1533
+ ".psd": "design",
1534
+ ".indd": "design",
1535
+ ".svg": "design",
1536
+ ".afdesign": "design",
1537
+ ".afphoto": "design",
1538
+ // business / ops
1539
+ ".xlsx": "business",
1540
+ ".xls": "business",
1541
+ ".csv": "business",
1542
+ ".numbers": "business",
1543
+ ".ods": "business",
1544
+ ".pptx": "business",
1545
+ ".ppt": "business",
1546
+ ".key": "business",
1547
+ ".docx": "business",
1548
+ ".doc": "business"
1549
+ };
1550
+ SOFTWARE_MANIFESTS = [
1551
+ "package.json",
1552
+ "Cargo.toml",
1553
+ "pyproject.toml",
1554
+ "setup.py",
1555
+ "Gemfile",
1556
+ "go.mod",
1557
+ "pom.xml",
1558
+ "build.gradle",
1559
+ "build.gradle.kts",
1560
+ "composer.json",
1561
+ "Package.swift",
1562
+ "mix.exs",
1563
+ "rebar.config",
1564
+ "pubspec.yaml",
1565
+ "CMakeLists.txt",
1566
+ "Makefile"
1567
+ ];
1568
+ BOOK_DIRS = ["manuscripts", "manuscript", "drafts", "draft", "chapters", "book"];
1569
+ CONTENT_DIRS = ["content", "posts", "articles", "blog", "newsletter", "social"];
1570
+ BUSINESS_DIRS = ["strategy", "financials", "finance", "ops", "runbooks", "sop", "sops", "ops-runbooks"];
1571
+ DESIGN_DIRS = ["designs", "design", "assets", "mockups", "wireframes", "figma"];
1572
+ FILENAME_PATTERNS = [
1573
+ { re: /^chapter[-_]?\d+/i, domain: "book", weight: 0.4 },
1574
+ { re: /^ch\d+/i, domain: "book", weight: 0.3 },
1575
+ { re: /^brand[-_]voice/i, domain: "content", weight: 0.4 },
1576
+ { re: /^seo[-_]/i, domain: "content", weight: 0.2 },
1577
+ { re: /^post[-_]/i, domain: "content", weight: 0.2 },
1578
+ { re: /^figma[-_]export/i, domain: "design", weight: 0.4 },
1579
+ { re: /^wireframe/i, domain: "design", weight: 0.3 }
1580
+ ];
1581
+ _c9AvailableCache = null;
1582
+ }
1583
+ });
1584
+
1585
+ // ../mcp-server/src/gate-result.js
1586
+ import { mkdir, writeFile } from "node:fs/promises";
1587
+ import { basename, dirname as dirname2, join as join6 } from "node:path";
1588
+ async function emitGateResult(gateOpts, context = {}) {
1589
+ if (gateOpts === null || typeof gateOpts !== "object") {
1590
+ throw new TypeError("emitGateResult: gateOpts must be an object");
1591
+ }
1592
+ const projectType = typeof context.project_type === "string" && context.project_type.length > 0 ? context.project_type : await resolveProjectType(context.projectRoot);
1593
+ const result = {
1594
+ schema_version: SCHEMA_VERSION,
1595
+ gate_id: makeGateId(gateOpts.gate),
1596
+ gate: gateOpts.gate,
1597
+ status: gateOpts.status,
1598
+ project_type: projectType,
1599
+ lenses: Array.isArray(gateOpts.lenses) ? gateOpts.lenses : [],
1600
+ affected_artifacts: Array.isArray(gateOpts.affected_artifacts) ? gateOpts.affected_artifacts : [],
1601
+ accounting: gateOpts.accounting,
1602
+ remediation: Array.isArray(gateOpts.remediation) ? gateOpts.remediation : [],
1603
+ receipts_ref: gateOpts.receipts_ref === void 0 ? null : gateOpts.receipts_ref,
1604
+ supersedes: gateOpts.supersedes === void 0 ? null : gateOpts.supersedes,
1605
+ emitted_at: (/* @__PURE__ */ new Date()).toISOString()
1606
+ };
1607
+ const { valid, errors } = validateGateResult(result);
1608
+ if (!valid) {
1609
+ throw new Error(
1610
+ `emitGateResult: invalid gate-result \u2014 ${errors.join("; ")}`
1611
+ );
1612
+ }
1613
+ makeReceipt(result, {
1614
+ projectRoot: typeof context.projectRoot === "string" && context.projectRoot.length > 0 ? context.projectRoot : process.cwd()
1615
+ }).catch(() => {
1616
+ });
1617
+ return formatGateResult(result);
1618
+ }
1619
+ async function makeReceipt(gateResult, opts = {}) {
1620
+ try {
1621
+ if (!gateResult || typeof gateResult !== "object") return;
1622
+ const gateId = typeof gateResult.gate_id === "string" ? gateResult.gate_id : null;
1623
+ if (!gateId) return;
1624
+ if (!RECEIPT_GATE_ID_PATTERN.test(gateId)) {
1625
+ try {
1626
+ process.stderr.write(
1627
+ `ijfw: makeReceipt rejected unsafe gate_id "${gateId}"
1628
+ `
1629
+ );
1630
+ } catch {
1631
+ }
1632
+ return;
1633
+ }
1634
+ const safeId = basename(gateId);
1635
+ const root = typeof opts.projectRoot === "string" && opts.projectRoot.length > 0 ? opts.projectRoot : process.cwd();
1636
+ const receiptPath = join6(
1637
+ root,
1638
+ ".ijfw",
1639
+ "memory",
1640
+ "gate-receipts",
1641
+ `${safeId}.json`
1642
+ );
1643
+ await mkdir(dirname2(receiptPath), { recursive: true });
1644
+ const body = JSON.stringify(gateResult, null, 2) + "\n";
1645
+ await writeFile(receiptPath, body, "utf8");
1646
+ } catch (err) {
1647
+ const msg = err && err.message ? err.message : String(err);
1648
+ try {
1649
+ process.stderr.write(`ijfw: gate-result receipt write failed: ${msg}
1650
+ `);
1651
+ } catch {
1652
+ }
1653
+ }
1654
+ }
1655
+ async function resolveProjectType(projectRoot) {
1656
+ try {
1657
+ const root = typeof projectRoot === "string" && projectRoot.length > 0 ? projectRoot : process.cwd();
1658
+ const detected = detect(root);
1659
+ if (detected && typeof detected.primary_type === "string" && detected.primary_type.length > 0) {
1660
+ return detected.primary_type;
1661
+ }
1662
+ if (detected && typeof detected.type === "string" && detected.type.length > 0) {
1663
+ return detected.type;
1664
+ }
1665
+ return "unknown";
1666
+ } catch {
1667
+ return "unknown";
1668
+ }
1669
+ }
1670
+ var RECEIPT_GATE_ID_PATTERN;
1671
+ var init_gate_result = __esm({
1672
+ "../mcp-server/src/gate-result.js"() {
1673
+ init_gate_result_schema();
1674
+ init_project_type_detector();
1675
+ RECEIPT_GATE_ID_PATTERN = /^[a-z][a-z0-9-]+$/;
1676
+ }
1677
+ });
1678
+
689
1679
  // src/preflight/gates/audit-ci.js
690
1680
  var audit_ci_exports = {};
691
1681
  __export(audit_ci_exports, {
@@ -695,7 +1685,7 @@ __export(audit_ci_exports, {
695
1685
  severity: () => severity7
696
1686
  });
697
1687
  import { spawnSync as spawnSync7 } from "node:child_process";
698
- import { join as join4 } from "node:path";
1688
+ import { join as join7 } from "node:path";
699
1689
  function parseAuditReport(output) {
700
1690
  const start = output.indexOf("{");
701
1691
  if (start === -1) return null;
@@ -726,7 +1716,7 @@ async function run7(ctx) {
726
1716
  ["audit", "--audit-level=high", "--json"],
727
1717
  {
728
1718
  encoding: "utf8",
729
- cwd: join4(ctx.repoRoot, dir),
1719
+ cwd: join7(ctx.repoRoot, dir),
730
1720
  timeout: 6e4
731
1721
  }
732
1722
  );
@@ -737,36 +1727,63 @@ async function run7(ctx) {
737
1727
  });
738
1728
  const durationMs = Date.now() - t0;
739
1729
  const failed = runs.filter((r) => !r.report || r.highCritical > 0);
740
- if (failed.length === 0) {
741
- return {
742
- name: "audit-ci",
743
- status: "PASS",
744
- message: "audit-ci: no high/critical vulnerabilities in installer or mcp-server",
745
- details: runs.map((r) => `${r.dir}: pass`),
746
- durationMs
747
- };
1730
+ const status = failed.length === 0 ? "PASS" : "FAIL";
1731
+ const message = status === "PASS" ? "audit-ci: no high/critical vulnerabilities in installer or mcp-server" : "audit-ci: high or critical vulnerabilities found";
1732
+ let details;
1733
+ if (status === "PASS") {
1734
+ details = runs.map((r) => `${r.dir}: pass`);
1735
+ } else {
1736
+ const lines = [];
1737
+ for (const r of failed) {
1738
+ if (!r.report) {
1739
+ lines.push(`${r.dir}: audit report unavailable`);
1740
+ lines.push(...r.output.split("\n").filter(Boolean).slice(0, 10));
1741
+ continue;
1742
+ }
1743
+ lines.push(`${r.dir}: ${r.highCritical} high/critical advisory item(s)`);
1744
+ lines.push(...vulnerableNames(r.report).slice(0, 10));
1745
+ }
1746
+ details = lines.slice(0, 20);
748
1747
  }
749
- const lines = [];
750
- for (const r of failed) {
751
- if (!r.report) {
752
- lines.push(`${r.dir}: audit report unavailable`);
753
- lines.push(...r.output.split("\n").filter(Boolean).slice(0, 10));
754
- continue;
1748
+ try {
1749
+ const block = await emitGateResult(
1750
+ {
1751
+ gate: "preflight:audit-ci",
1752
+ status,
1753
+ lenses: [],
1754
+ affected_artifacts: [],
1755
+ accounting: {
1756
+ duration_ms: durationMs,
1757
+ lenses_invoked: 0,
1758
+ cost_usd: null
1759
+ },
1760
+ remediation: []
1761
+ },
1762
+ ctx && ctx.repoRoot ? { projectRoot: ctx.repoRoot } : {}
1763
+ );
1764
+ if (typeof block === "string" && block.length > 0) {
1765
+ details = [...details, block];
1766
+ }
1767
+ } catch (err) {
1768
+ const msg = err && err.message ? err.message : String(err);
1769
+ try {
1770
+ process.stderr.write(`ijfw: preflight:audit-ci gate-result emit failed: ${msg}
1771
+ `);
1772
+ } catch {
755
1773
  }
756
- lines.push(`${r.dir}: ${r.highCritical} high/critical advisory item(s)`);
757
- lines.push(...vulnerableNames(r.report).slice(0, 10));
758
1774
  }
759
1775
  return {
760
1776
  name: "audit-ci",
761
- status: "FAIL",
762
- message: "audit-ci: high or critical vulnerabilities found",
763
- details: lines.slice(0, 20),
1777
+ status,
1778
+ message,
1779
+ details,
764
1780
  durationMs
765
1781
  };
766
1782
  }
767
1783
  var name7, severity7, parallel7;
768
1784
  var init_audit_ci = __esm({
769
1785
  "src/preflight/gates/audit-ci.js"() {
1786
+ init_gate_result();
770
1787
  name7 = "audit-ci";
771
1788
  severity7 = "blocking";
772
1789
  parallel7 = true;
@@ -828,7 +1845,7 @@ __export(license_check_exports, {
828
1845
  severity: () => severity9
829
1846
  });
830
1847
  import { spawnSync as spawnSync9 } from "node:child_process";
831
- import { join as join5 } from "node:path";
1848
+ import { join as join8 } from "node:path";
832
1849
  async function run9(ctx) {
833
1850
  const t0 = Date.now();
834
1851
  const ver = ctx.versions["license-checker"] || "latest";
@@ -837,7 +1854,7 @@ async function run9(ctx) {
837
1854
  ["--yes", `license-checker@${ver}`, "--onlyAllow", ALLOWED, "--production"],
838
1855
  {
839
1856
  encoding: "utf8",
840
- cwd: join5(ctx.repoRoot, "installer"),
1857
+ cwd: join8(ctx.repoRoot, "installer"),
841
1858
  timeout: 3e4
842
1859
  }
843
1860
  );
@@ -880,12 +1897,12 @@ __export(pack_smoke_exports, {
880
1897
  severity: () => severity10
881
1898
  });
882
1899
  import { spawnSync as spawnSync10 } from "node:child_process";
883
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, mkdirSync, writeFileSync as writeFileSync2, readdirSync as readdirSync3, existsSync } from "node:fs";
884
- import { join as join6, resolve } from "node:path";
1900
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync4, existsSync as existsSync3 } from "node:fs";
1901
+ import { join as join9, resolve } from "node:path";
885
1902
  import { tmpdir as tmpdir2 } from "node:os";
886
1903
  async function run10(ctx) {
887
1904
  const t0 = Date.now();
888
- const installerDir = join6(ctx.repoRoot, "installer");
1905
+ const installerDir = join9(ctx.repoRoot, "installer");
889
1906
  const build = spawnSync10("npm", ["run", "build"], {
890
1907
  encoding: "utf8",
891
1908
  cwd: installerDir,
@@ -927,13 +1944,13 @@ async function run10(ctx) {
927
1944
  };
928
1945
  }
929
1946
  const tarballPath = resolve(installerDir, tarball);
930
- const tmpRoot = mkdtempSync2(join6(tmpdir2(), "ijfw-pack-smoke-"));
931
- const fakeHome = join6(tmpRoot, "home");
932
- const installDir = join6(tmpRoot, "install");
933
- mkdirSync(fakeHome, { recursive: true });
934
- mkdirSync(installDir, { recursive: true });
1947
+ const tmpRoot = mkdtempSync2(join9(tmpdir2(), "ijfw-pack-smoke-"));
1948
+ const fakeHome = join9(tmpRoot, "home");
1949
+ const installDir = join9(tmpRoot, "install");
1950
+ mkdirSync3(fakeHome, { recursive: true });
1951
+ mkdirSync3(installDir, { recursive: true });
935
1952
  try {
936
- writeFileSync2(join6(installDir, "package.json"), JSON.stringify({ name: "smoke-test", version: "1.0.0", type: "module" }));
1953
+ writeFileSync4(join9(installDir, "package.json"), JSON.stringify({ name: "smoke-test", version: "1.0.0", type: "module" }));
937
1954
  const install = spawnSync10("npm", ["install", "--no-save", tarballPath], {
938
1955
  encoding: "utf8",
939
1956
  cwd: installDir,
@@ -951,25 +1968,25 @@ async function run10(ctx) {
951
1968
  };
952
1969
  }
953
1970
  const binCandidates = [
954
- join6(installDir, "node_modules", ".bin", "ijfw"),
955
- join6(installDir, "node_modules", ".bin", "ijfw-install")
1971
+ join9(installDir, "node_modules", ".bin", "ijfw"),
1972
+ join9(installDir, "node_modules", ".bin", "ijfw-install")
956
1973
  ];
957
1974
  let binPath = null;
958
1975
  for (const c2 of binCandidates) {
959
- if (existsSync(c2)) {
1976
+ if (existsSync3(c2)) {
960
1977
  binPath = c2;
961
1978
  break;
962
1979
  }
963
1980
  }
964
1981
  if (!binPath) {
965
- const binDir = join6(installDir, "node_modules", ".bin");
1982
+ const binDir = join9(installDir, "node_modules", ".bin");
966
1983
  let entries = [];
967
1984
  try {
968
- entries = readdirSync3(binDir);
1985
+ entries = readdirSync4(binDir);
969
1986
  } catch {
970
1987
  }
971
1988
  const found = entries.find((e) => e.startsWith("ijfw"));
972
- if (found) binPath = join6(binDir, found);
1989
+ if (found) binPath = join9(binDir, found);
973
1990
  }
974
1991
  if (!binPath) {
975
1992
  return {
@@ -1032,12 +2049,12 @@ __export(upgrade_smoke_exports, {
1032
2049
  severity: () => severity11
1033
2050
  });
1034
2051
  import { spawnSync as spawnSync11 } from "node:child_process";
1035
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
1036
- import { join as join7, resolve as resolve2 } from "node:path";
2052
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, readFileSync as readFileSync4, existsSync as existsSync4 } from "node:fs";
2053
+ import { join as join10, resolve as resolve2 } from "node:path";
1037
2054
  import { tmpdir as tmpdir3 } from "node:os";
1038
2055
  async function run11(ctx) {
1039
2056
  const t0 = Date.now();
1040
- const installerDir = join7(ctx.repoRoot, "installer");
2057
+ const installerDir = join10(ctx.repoRoot, "installer");
1041
2058
  const build = spawnSync11("npm", ["run", "build"], {
1042
2059
  encoding: "utf8",
1043
2060
  cwd: installerDir,
@@ -1070,15 +2087,15 @@ async function run11(ctx) {
1070
2087
  }
1071
2088
  const tarball = pack.stdout.trim();
1072
2089
  const tarballPath = resolve2(installerDir, tarball);
1073
- const tmpRoot = mkdtempSync3(join7(tmpdir3(), "ijfw-upgrade-smoke-"));
1074
- const fakeHome = join7(tmpRoot, "home");
1075
- const installDir = join7(tmpRoot, "install");
1076
- mkdirSync2(fakeHome, { recursive: true });
1077
- mkdirSync2(installDir, { recursive: true });
1078
- const claudeDir = join7(fakeHome, ".claude");
1079
- mkdirSync2(claudeDir, { recursive: true });
2090
+ const tmpRoot = mkdtempSync3(join10(tmpdir3(), "ijfw-upgrade-smoke-"));
2091
+ const fakeHome = join10(tmpRoot, "home");
2092
+ const installDir = join10(tmpRoot, "install");
2093
+ mkdirSync4(fakeHome, { recursive: true });
2094
+ mkdirSync4(installDir, { recursive: true });
2095
+ const claudeDir = join10(fakeHome, ".claude");
2096
+ mkdirSync4(claudeDir, { recursive: true });
1080
2097
  try {
1081
- writeFileSync3(join7(installDir, "package.json"), JSON.stringify({ name: "upgrade-smoke", version: "1.0.0", type: "module" }));
2098
+ writeFileSync5(join10(installDir, "package.json"), JSON.stringify({ name: "upgrade-smoke", version: "1.0.0", type: "module" }));
1082
2099
  const install = spawnSync11("npm", ["install", "--no-save", tarballPath], {
1083
2100
  encoding: "utf8",
1084
2101
  cwd: installDir,
@@ -1096,12 +2113,12 @@ async function run11(ctx) {
1096
2113
  };
1097
2114
  }
1098
2115
  const binCandidates = [
1099
- join7(installDir, "node_modules", ".bin", "ijfw-install"),
1100
- join7(installDir, "node_modules", ".bin", "ijfw")
2116
+ join10(installDir, "node_modules", ".bin", "ijfw-install"),
2117
+ join10(installDir, "node_modules", ".bin", "ijfw")
1101
2118
  ];
1102
2119
  let installerBin = null;
1103
2120
  for (const c2 of binCandidates) {
1104
- if (existsSync2(c2)) {
2121
+ if (existsSync4(c2)) {
1105
2122
  installerBin = c2;
1106
2123
  break;
1107
2124
  }
@@ -1115,11 +2132,11 @@ async function run11(ctx) {
1115
2132
  durationMs: Date.now() - t0
1116
2133
  };
1117
2134
  }
1118
- const settingsPath = join7(claudeDir, "settings.json");
1119
- if (existsSync2(settingsPath)) {
2135
+ const settingsPath = join10(claudeDir, "settings.json");
2136
+ if (existsSync4(settingsPath)) {
1120
2137
  let settings;
1121
2138
  try {
1122
- settings = JSON.parse(readFileSync2(settingsPath, "utf8"));
2139
+ settings = JSON.parse(readFileSync4(settingsPath, "utf8"));
1123
2140
  } catch (e) {
1124
2141
  return {
1125
2142
  name: "upgrade-smoke",
@@ -1140,9 +2157,9 @@ async function run11(ctx) {
1140
2157
  };
1141
2158
  }
1142
2159
  }
1143
- const marketplaceSrc = join7(installerDir, "src", "marketplace.js");
1144
- if (existsSync2(marketplaceSrc)) {
1145
- const src = readFileSync2(marketplaceSrc, "utf8");
2160
+ const marketplaceSrc = join10(installerDir, "src", "marketplace.js");
2161
+ if (existsSync4(marketplaceSrc)) {
2162
+ const src = readFileSync4(marketplaceSrc, "utf8");
1146
2163
  const registersCorrectKey = src.includes("'ijfw@ijfw'") || src.includes('"ijfw@ijfw"');
1147
2164
  const registersWrongKey = /enabledPlugins\[['"]ijfw-core@ijfw['"]\]\s*=\s*true/.test(src);
1148
2165
  if (!registersCorrectKey) {
@@ -1197,9 +2214,9 @@ var preflight_exports = {};
1197
2214
  __export(preflight_exports, {
1198
2215
  runPreflightCommand: () => runPreflightCommand
1199
2216
  });
1200
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
1201
- import { join as join8, dirname, resolve as resolve3 } from "node:path";
1202
- import { fileURLToPath } from "node:url";
2217
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
2218
+ import { join as join11, dirname as dirname3, resolve as resolve3 } from "node:path";
2219
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1203
2220
  function printHelp() {
1204
2221
  console.log(`
1205
2222
  ijfw preflight -- 11-gate quality pipeline
@@ -1235,13 +2252,13 @@ SLO
1235
2252
  }
1236
2253
  function loadVersions(repoRoot2) {
1237
2254
  const candidates = [
1238
- join8(repoRoot2, "preflight-versions.json"),
1239
- join8(repoRoot2, ".ijfw", "preflight-versions.json")
2255
+ join11(repoRoot2, "preflight-versions.json"),
2256
+ join11(repoRoot2, ".ijfw", "preflight-versions.json")
1240
2257
  ];
1241
2258
  for (const f of candidates) {
1242
- if (existsSync3(f)) {
2259
+ if (existsSync5(f)) {
1243
2260
  try {
1244
- return JSON.parse(readFileSync3(f, "utf8"));
2261
+ return JSON.parse(readFileSync5(f, "utf8"));
1245
2262
  } catch {
1246
2263
  }
1247
2264
  }
@@ -1300,7 +2317,7 @@ async function runPreflightCommand(argv, repoRoot2) {
1300
2317
  function defaultRepoRoot() {
1301
2318
  let dir = __dirname;
1302
2319
  for (let i = 0; i < 8; i++) {
1303
- if (existsSync3(join8(dir, "package.json")) && existsSync3(join8(dir, "mcp-server"))) return dir;
2320
+ if (existsSync5(join11(dir, "package.json")) && existsSync5(join11(dir, "mcp-server"))) return dir;
1304
2321
  const next = resolve3(dir, "..");
1305
2322
  if (next === dir) break;
1306
2323
  dir = next;
@@ -1311,8 +2328,8 @@ var __dirname;
1311
2328
  var init_preflight = __esm({
1312
2329
  async "src/preflight.js"() {
1313
2330
  init_runner();
1314
- __dirname = dirname(fileURLToPath(import.meta.url));
1315
- if (process.argv[1] && resolve3(process.argv[1]) === fileURLToPath(import.meta.url)) {
2331
+ __dirname = dirname3(fileURLToPath2(import.meta.url));
2332
+ if (process.argv[1] && resolve3(process.argv[1]) === fileURLToPath2(import.meta.url)) {
1316
2333
  await runPreflightCommand(process.argv, defaultRepoRoot());
1317
2334
  }
1318
2335
  }
@@ -2578,30 +3595,30 @@ Please report this to https://github.com/markedjs/marked.`, e) {
2578
3595
  });
2579
3596
 
2580
3597
  // src/ijfw.js
2581
- import { dirname as dirname2, join as join9, resolve as resolve4, basename } from "node:path";
2582
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2583
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, copyFileSync, readdirSync as readdirSync4, rmSync as rmSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
3598
+ import { dirname as dirname4, join as join12, resolve as resolve4, basename as basename2 } from "node:path";
3599
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
3600
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, copyFileSync as copyFileSync3, readdirSync as readdirSync5, rmSync as rmSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
2584
3601
  import { homedir, platform } from "node:os";
2585
3602
  import { spawnSync as spawnSync12 } from "node:child_process";
2586
- var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
3603
+ var __dirname2 = dirname4(fileURLToPath3(import.meta.url));
2587
3604
  function repoRoot() {
2588
3605
  let dir = __dirname2;
2589
3606
  for (let i = 0; i < 6; i++) {
2590
- if (existsSync4(join9(dir, "package.json")) && existsSync4(join9(dir, ".git"))) return dir;
3607
+ if (existsSync6(join12(dir, "package.json")) && existsSync6(join12(dir, ".git"))) return dir;
2591
3608
  dir = resolve4(dir, "..");
2592
3609
  }
2593
3610
  return process.cwd();
2594
3611
  }
2595
3612
  function findInternalAsset(...rel) {
2596
3613
  const root = repoRoot();
2597
- const ijfwHome = join9(homedir(), ".ijfw");
2598
- const candidates = [join9(root, ...rel), join9(ijfwHome, ...rel)];
2599
- return candidates.find((p) => existsSync4(p)) || null;
3614
+ const ijfwHome = join12(homedir(), ".ijfw");
3615
+ const candidates = [join12(root, ...rel), join12(ijfwHome, ...rel)];
3616
+ return candidates.find((p) => existsSync6(p)) || null;
2600
3617
  }
2601
3618
  function readDashboardPort() {
2602
- const portFile = join9(homedir(), ".ijfw", "dashboard.port");
3619
+ const portFile = join12(homedir(), ".ijfw", "dashboard.port");
2603
3620
  try {
2604
- const port = Number.parseInt(readFileSync4(portFile, "utf8").trim(), 10);
3621
+ const port = Number.parseInt(readFileSync6(portFile, "utf8").trim(), 10);
2605
3622
  return Number.isFinite(port) ? port : 37891;
2606
3623
  } catch {
2607
3624
  return 37891;
@@ -2646,7 +3663,9 @@ var ORCHESTRATOR_COMMANDS = /* @__PURE__ */ new Set([
2646
3663
  "memory-consent",
2647
3664
  "memory-why",
2648
3665
  "metrics",
2649
- "mode"
3666
+ "mode",
3667
+ "override",
3668
+ "extension"
2650
3669
  ]);
2651
3670
  function printHelp2() {
2652
3671
  console.log(`
@@ -2682,10 +3701,10 @@ function doctorCheck(cmd, args) {
2682
3701
  }
2683
3702
  function findCli() {
2684
3703
  const candidates = [
2685
- join9(repoRoot(), "mcp-server", "src", "cross-orchestrator-cli.js"),
2686
- join9(homedir(), ".ijfw", "mcp-server", "src", "cross-orchestrator-cli.js")
3704
+ join12(repoRoot(), "mcp-server", "src", "cross-orchestrator-cli.js"),
3705
+ join12(homedir(), ".ijfw", "mcp-server", "src", "cross-orchestrator-cli.js")
2687
3706
  ];
2688
- return candidates.find((p) => existsSync4(p)) || null;
3707
+ return candidates.find((p) => existsSync6(p)) || null;
2689
3708
  }
2690
3709
  function delegateToCli(argTail) {
2691
3710
  const cli = findCli();
@@ -2704,8 +3723,8 @@ async function main() {
2704
3723
  const verbose = argv.slice(3).includes("--verbose");
2705
3724
  if (delegateToCli(argv.slice(2))) return;
2706
3725
  try {
2707
- const pkgPath = join9(__dirname2, "..", "package.json");
2708
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
3726
+ const pkgPath = join12(__dirname2, "..", "package.json");
3727
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
2709
3728
  console.log(`@ijfw/install@${pkg.version || "unknown"}`);
2710
3729
  if (verbose) {
2711
3730
  console.log(" (full --verbose details require a completed install: run ijfw install)");
@@ -2786,8 +3805,8 @@ async function main() {
2786
3805
  console.error(`'ijfw design ${designSub}' requires a completed IJFW install. Run: ijfw install`);
2787
3806
  process.exit(1);
2788
3807
  }
2789
- const contentDir = join9(homedir(), ".ijfw", "design-companion", "content");
2790
- mkdirSync3(contentDir, { recursive: true });
3808
+ const contentDir = join12(homedir(), ".ijfw", "design-companion", "content");
3809
+ mkdirSync5(contentDir, { recursive: true });
2791
3810
  if (designSub === "start" || designSub === "open") {
2792
3811
  const dashBin = findInternalAsset("mcp-server", "bin", "ijfw-dashboard");
2793
3812
  if (!dashBin) {
@@ -2829,18 +3848,18 @@ async function main() {
2829
3848
  console.error("Design companion accepts standalone .html files.");
2830
3849
  process.exit(1);
2831
3850
  }
2832
- if (!existsSync4(abs)) {
3851
+ if (!existsSync6(abs)) {
2833
3852
  console.error(`File not found: ${abs}`);
2834
3853
  process.exit(1);
2835
3854
  }
2836
- const dest = join9(contentDir, basename(abs));
2837
- copyFileSync(abs, dest);
3855
+ const dest = join12(contentDir, basename2(abs));
3856
+ copyFileSync3(abs, dest);
2838
3857
  console.log(`Design pushed: ${dest}`);
2839
3858
  }
2840
3859
  console.log(`Preview: http://localhost:${readDashboardPort()}/design`);
2841
3860
  } else if (designSub === "clear") {
2842
- const files = readdirSync4(contentDir);
2843
- for (const f of files) rmSync4(join9(contentDir, f), { force: true });
3861
+ const files = readdirSync5(contentDir);
3862
+ for (const f of files) rmSync4(join12(contentDir, f), { force: true });
2844
3863
  console.log("Design companion content cleared.");
2845
3864
  } else {
2846
3865
  console.log("ijfw design -- Manage live preview and durable design intelligence.");
@@ -2853,26 +3872,26 @@ async function main() {
2853
3872
  case "help": {
2854
3873
  const wantsBrowser = argv.slice(3).includes("--browser");
2855
3874
  const candidates = [
2856
- join9(repoRoot(), "docs", "GUIDE.md"),
3875
+ join12(repoRoot(), "docs", "GUIDE.md"),
2857
3876
  resolve4(__dirname2, "..", "docs", "GUIDE.md"),
2858
- join9(homedir(), ".ijfw", "docs", "GUIDE.md")
3877
+ join12(homedir(), ".ijfw", "docs", "GUIDE.md")
2859
3878
  ];
2860
- const guidePath = candidates.find((p) => existsSync4(p));
3879
+ const guidePath = candidates.find((p) => existsSync6(p));
2861
3880
  if (!guidePath) {
2862
3881
  console.error("[ijfw] Guide not found. Run `ijfw install` to fetch the full guide, or visit https://gitlab.com/therealseandonahoe/ijfw/-/blob/main/docs/GUIDE.md");
2863
3882
  process.exit(1);
2864
3883
  }
2865
3884
  if (wantsBrowser) {
2866
3885
  const { marked } = await Promise.resolve().then(() => (init_marked_esm(), marked_esm_exports));
2867
- const assetsSrc = join9(dirname2(guidePath), "guide", "assets");
2868
- const outDir = join9(homedir(), ".ijfw", "guide");
2869
- mkdirSync3(join9(outDir, "assets"), { recursive: true });
2870
- if (existsSync4(assetsSrc)) {
2871
- for (const f of readdirSync4(assetsSrc)) {
2872
- copyFileSync(join9(assetsSrc, f), join9(outDir, "assets", f));
3886
+ const assetsSrc = join12(dirname4(guidePath), "guide", "assets");
3887
+ const outDir = join12(homedir(), ".ijfw", "guide");
3888
+ mkdirSync5(join12(outDir, "assets"), { recursive: true });
3889
+ if (existsSync6(assetsSrc)) {
3890
+ for (const f of readdirSync5(assetsSrc)) {
3891
+ copyFileSync3(join12(assetsSrc, f), join12(outDir, "assets", f));
2873
3892
  }
2874
3893
  }
2875
- const md = readFileSync4(guidePath, "utf8").replace(/\(guide\/assets\//g, "(assets/");
3894
+ const md = readFileSync6(guidePath, "utf8").replace(/\(guide\/assets\//g, "(assets/");
2876
3895
  const rendered = marked.parse(md, { gfm: true, breaks: false });
2877
3896
  const html = `<!doctype html>
2878
3897
  <html lang="en"><head>
@@ -2889,8 +3908,8 @@ async function main() {
2889
3908
  table{display:table;width:100%}
2890
3909
  </style>
2891
3910
  </head><body><div class="wrap markdown-body">${rendered}</div></body></html>`;
2892
- const outHtml = join9(outDir, "index.html");
2893
- writeFileSync4(outHtml, html);
3911
+ const outHtml = join12(outDir, "index.html");
3912
+ writeFileSync6(outHtml, html);
2894
3913
  const opener = platform() === "darwin" ? "open" : platform() === "win32" ? "start" : "xdg-open";
2895
3914
  spawnSync12(opener, [outHtml], { stdio: "ignore", detached: true });
2896
3915
  console.log(`[ijfw] Guide opened in your browser.`);
@@ -2901,10 +3920,10 @@ async function main() {
2901
3920
  if (hasLess) {
2902
3921
  const lessRes = spawnSync12("less", ["-R", guidePath], { stdio: "inherit" });
2903
3922
  if (lessRes.status !== 0 && lessRes.status !== null) {
2904
- process.stdout.write(readFileSync4(guidePath, "utf8"));
3923
+ process.stdout.write(readFileSync6(guidePath, "utf8"));
2905
3924
  }
2906
3925
  } else {
2907
- process.stdout.write(readFileSync4(guidePath, "utf8"));
3926
+ process.stdout.write(readFileSync6(guidePath, "utf8"));
2908
3927
  }
2909
3928
  process.exit(0);
2910
3929
  break;