@agent-profile/web 0.1.2

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.
Files changed (150) hide show
  1. package/README.md +110 -0
  2. package/build/client/_app/immutable/assets/0.BG6jUcBX.css +1 -0
  3. package/build/client/_app/immutable/assets/0.BG6jUcBX.css.br +0 -0
  4. package/build/client/_app/immutable/assets/0.BG6jUcBX.css.gz +0 -0
  5. package/build/client/_app/immutable/chunks/B-io8J2C.js +1 -0
  6. package/build/client/_app/immutable/chunks/B-io8J2C.js.br +2 -0
  7. package/build/client/_app/immutable/chunks/B-io8J2C.js.gz +0 -0
  8. package/build/client/_app/immutable/chunks/BFYQcBYR.js +1 -0
  9. package/build/client/_app/immutable/chunks/BFYQcBYR.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/BFYQcBYR.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/D5BQ10GJ.js +3 -0
  12. package/build/client/_app/immutable/chunks/D5BQ10GJ.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/D5BQ10GJ.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/DAWBIq3g.js +2 -0
  15. package/build/client/_app/immutable/chunks/DAWBIq3g.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/DAWBIq3g.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/DCJP-V9E.js +3 -0
  18. package/build/client/_app/immutable/chunks/DCJP-V9E.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/DCJP-V9E.js.gz +0 -0
  20. package/build/client/_app/immutable/chunks/I8rQr1kY.js +1 -0
  21. package/build/client/_app/immutable/chunks/I8rQr1kY.js.br +1 -0
  22. package/build/client/_app/immutable/chunks/I8rQr1kY.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/SOrpfga1.js +1 -0
  24. package/build/client/_app/immutable/chunks/SOrpfga1.js.br +0 -0
  25. package/build/client/_app/immutable/chunks/SOrpfga1.js.gz +0 -0
  26. package/build/client/_app/immutable/chunks/rbd2FHQ6.js +1 -0
  27. package/build/client/_app/immutable/chunks/rbd2FHQ6.js.br +2 -0
  28. package/build/client/_app/immutable/chunks/rbd2FHQ6.js.gz +0 -0
  29. package/build/client/_app/immutable/entry/app.wvdb3njx.js +2 -0
  30. package/build/client/_app/immutable/entry/app.wvdb3njx.js.br +0 -0
  31. package/build/client/_app/immutable/entry/app.wvdb3njx.js.gz +0 -0
  32. package/build/client/_app/immutable/entry/start.CYyClicm.js +1 -0
  33. package/build/client/_app/immutable/entry/start.CYyClicm.js.br +1 -0
  34. package/build/client/_app/immutable/entry/start.CYyClicm.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/0.DI161f18.js +1 -0
  36. package/build/client/_app/immutable/nodes/0.DI161f18.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/0.DI161f18.js.gz +0 -0
  38. package/build/client/_app/immutable/nodes/1.CZb1wh8q.js +1 -0
  39. package/build/client/_app/immutable/nodes/1.CZb1wh8q.js.br +3 -0
  40. package/build/client/_app/immutable/nodes/1.CZb1wh8q.js.gz +0 -0
  41. package/build/client/_app/immutable/nodes/10.BOQgahuX.js +2 -0
  42. package/build/client/_app/immutable/nodes/10.BOQgahuX.js.br +0 -0
  43. package/build/client/_app/immutable/nodes/10.BOQgahuX.js.gz +0 -0
  44. package/build/client/_app/immutable/nodes/11.BjwWla8T.js +1 -0
  45. package/build/client/_app/immutable/nodes/11.BjwWla8T.js.br +0 -0
  46. package/build/client/_app/immutable/nodes/11.BjwWla8T.js.gz +0 -0
  47. package/build/client/_app/immutable/nodes/2.BmqkJNGt.js +9 -0
  48. package/build/client/_app/immutable/nodes/2.BmqkJNGt.js.br +0 -0
  49. package/build/client/_app/immutable/nodes/2.BmqkJNGt.js.gz +0 -0
  50. package/build/client/_app/immutable/nodes/3.NnDkXnSG.js +2 -0
  51. package/build/client/_app/immutable/nodes/3.NnDkXnSG.js.br +0 -0
  52. package/build/client/_app/immutable/nodes/3.NnDkXnSG.js.gz +0 -0
  53. package/build/client/_app/immutable/nodes/4.8sZ5zCiS.js +4 -0
  54. package/build/client/_app/immutable/nodes/4.8sZ5zCiS.js.br +0 -0
  55. package/build/client/_app/immutable/nodes/4.8sZ5zCiS.js.gz +0 -0
  56. package/build/client/_app/immutable/nodes/5.CSOalQ9R.js +2 -0
  57. package/build/client/_app/immutable/nodes/5.CSOalQ9R.js.br +0 -0
  58. package/build/client/_app/immutable/nodes/5.CSOalQ9R.js.gz +0 -0
  59. package/build/client/_app/immutable/nodes/6.BMBuGW7y.js +5 -0
  60. package/build/client/_app/immutable/nodes/6.BMBuGW7y.js.br +0 -0
  61. package/build/client/_app/immutable/nodes/6.BMBuGW7y.js.gz +0 -0
  62. package/build/client/_app/immutable/nodes/7.CG2RK2bH.js +4 -0
  63. package/build/client/_app/immutable/nodes/7.CG2RK2bH.js.br +0 -0
  64. package/build/client/_app/immutable/nodes/7.CG2RK2bH.js.gz +0 -0
  65. package/build/client/_app/immutable/nodes/8.Dzq0LDIw.js +1 -0
  66. package/build/client/_app/immutable/nodes/8.Dzq0LDIw.js.br +0 -0
  67. package/build/client/_app/immutable/nodes/8.Dzq0LDIw.js.gz +0 -0
  68. package/build/client/_app/immutable/nodes/9.CnkwRoxn.js +5 -0
  69. package/build/client/_app/immutable/nodes/9.CnkwRoxn.js.br +0 -0
  70. package/build/client/_app/immutable/nodes/9.CnkwRoxn.js.gz +0 -0
  71. package/build/client/_app/version.json +1 -0
  72. package/build/client/_app/version.json.br +0 -0
  73. package/build/client/_app/version.json.gz +0 -0
  74. package/build/env.js +94 -0
  75. package/build/handler.js +1494 -0
  76. package/build/index.js +345 -0
  77. package/build/server/chunks/0-u7mEipCH.js +110 -0
  78. package/build/server/chunks/0-u7mEipCH.js.map +1 -0
  79. package/build/server/chunks/1-BBGjCHyQ.js +9 -0
  80. package/build/server/chunks/1-BBGjCHyQ.js.map +1 -0
  81. package/build/server/chunks/10-B1aX7ZZ8.js +9 -0
  82. package/build/server/chunks/10-B1aX7ZZ8.js.map +1 -0
  83. package/build/server/chunks/11-4z6b2WB8.js +83 -0
  84. package/build/server/chunks/11-4z6b2WB8.js.map +1 -0
  85. package/build/server/chunks/2-CZ6isW8Z.js +9 -0
  86. package/build/server/chunks/2-CZ6isW8Z.js.map +1 -0
  87. package/build/server/chunks/3-CLWhM7Et.js +9 -0
  88. package/build/server/chunks/3-CLWhM7Et.js.map +1 -0
  89. package/build/server/chunks/4-BehBrXcu.js +146 -0
  90. package/build/server/chunks/4-BehBrXcu.js.map +1 -0
  91. package/build/server/chunks/5-C1YtO2Ru.js +9 -0
  92. package/build/server/chunks/5-C1YtO2Ru.js.map +1 -0
  93. package/build/server/chunks/6-DJ03bQJy.js +9 -0
  94. package/build/server/chunks/6-DJ03bQJy.js.map +1 -0
  95. package/build/server/chunks/7-CI__yhaj.js +73 -0
  96. package/build/server/chunks/7-CI__yhaj.js.map +1 -0
  97. package/build/server/chunks/8-BGx10Z6s.js +9 -0
  98. package/build/server/chunks/8-BGx10Z6s.js.map +1 -0
  99. package/build/server/chunks/9-LeS-ldcl.js +57 -0
  100. package/build/server/chunks/9-LeS-ldcl.js.map +1 -0
  101. package/build/server/chunks/Badge-BrV_lIru.js +24 -0
  102. package/build/server/chunks/Badge-BrV_lIru.js.map +1 -0
  103. package/build/server/chunks/SafetyBadge-BSDbVVMY.js +13 -0
  104. package/build/server/chunks/SafetyBadge-BSDbVVMY.js.map +1 -0
  105. package/build/server/chunks/_layout.svelte-Cg7oqdVB.js +214 -0
  106. package/build/server/chunks/_layout.svelte-Cg7oqdVB.js.map +1 -0
  107. package/build/server/chunks/_page.svelte-B0_5f_5-.js +86 -0
  108. package/build/server/chunks/_page.svelte-B0_5f_5-.js.map +1 -0
  109. package/build/server/chunks/_page.svelte-BS9xpx67.js +204 -0
  110. package/build/server/chunks/_page.svelte-BS9xpx67.js.map +1 -0
  111. package/build/server/chunks/_page.svelte-Bv-w7cKm.js +106 -0
  112. package/build/server/chunks/_page.svelte-Bv-w7cKm.js.map +1 -0
  113. package/build/server/chunks/_page.svelte-C8HqSRYY.js +55 -0
  114. package/build/server/chunks/_page.svelte-C8HqSRYY.js.map +1 -0
  115. package/build/server/chunks/_page.svelte-DRj4L7XZ.js +104 -0
  116. package/build/server/chunks/_page.svelte-DRj4L7XZ.js.map +1 -0
  117. package/build/server/chunks/_page.svelte-DXeUSVlF.js +14 -0
  118. package/build/server/chunks/_page.svelte-DXeUSVlF.js.map +1 -0
  119. package/build/server/chunks/_page.svelte-DbsvERRq.js +424 -0
  120. package/build/server/chunks/_page.svelte-DbsvERRq.js.map +1 -0
  121. package/build/server/chunks/_page.svelte-DuJcNgye.js +172 -0
  122. package/build/server/chunks/_page.svelte-DuJcNgye.js.map +1 -0
  123. package/build/server/chunks/_page.svelte-DyYKrY-D.js +92 -0
  124. package/build/server/chunks/_page.svelte-DyYKrY-D.js.map +1 -0
  125. package/build/server/chunks/_page.svelte-r2AHAn4f.js +134 -0
  126. package/build/server/chunks/_page.svelte-r2AHAn4f.js.map +1 -0
  127. package/build/server/chunks/client-DRY1rofW.js +23 -0
  128. package/build/server/chunks/client-DRY1rofW.js.map +1 -0
  129. package/build/server/chunks/dev-CrqJ3ZR3.js +4558 -0
  130. package/build/server/chunks/dev-CrqJ3ZR3.js.map +1 -0
  131. package/build/server/chunks/dist--t-UlYlZ.js +532 -0
  132. package/build/server/chunks/dist--t-UlYlZ.js.map +1 -0
  133. package/build/server/chunks/doctorSummary-CGjSiyKU.js +85 -0
  134. package/build/server/chunks/doctorSummary-CGjSiyKU.js.map +1 -0
  135. package/build/server/chunks/error.svelte-CvT76GCP.js +66 -0
  136. package/build/server/chunks/error.svelte-CvT76GCP.js.map +1 -0
  137. package/build/server/chunks/hooks.server-BGib69tn.js +38 -0
  138. package/build/server/chunks/hooks.server-BGib69tn.js.map +1 -0
  139. package/build/server/chunks/index-XyMIhwom.js +4727 -0
  140. package/build/server/chunks/index-XyMIhwom.js.map +1 -0
  141. package/build/server/chunks/internal-DDD35M_S.js +2068 -0
  142. package/build/server/chunks/internal-DDD35M_S.js.map +1 -0
  143. package/build/server/chunks/projectContext-C4-xYqFg.js +17311 -0
  144. package/build/server/chunks/projectContext-C4-xYqFg.js.map +1 -0
  145. package/build/server/index.js +5 -0
  146. package/build/server/index.js.map +1 -0
  147. package/build/server/manifest.js +118 -0
  148. package/build/server/manifest.js.map +1 -0
  149. package/build/shims.js +32 -0
  150. package/package.json +34 -0
@@ -0,0 +1,532 @@
1
+ import { p as parseProfileYaml, c as compileProfile, b as safeOutputPath, d as containsSecretLikeLiteral, v as validateLockfileText, e as createLockfileFile, s as sha256Hex, n as normalizeSafety, f as deriveEffectivePermissions } from './projectContext-C4-xYqFg.js';
2
+ import { readFile, readdir } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ //#region ../../packages/doctor/dist/doctor.js
6
+ var PROFILE_PATH = "ai-profile.yaml";
7
+ var LOCKFILE_PATH = "ai-profile.lock";
8
+ var SKILL_ROOTS = [".agents/skills", ".claude/skills"];
9
+ var SEVERITY_ORDER = {
10
+ error: 0,
11
+ warning: 1,
12
+ info: 2
13
+ };
14
+ async function runDoctor(request = {}) {
15
+ const rootDir = path.resolve(request.rootDir ?? ".");
16
+ const issues = [];
17
+ const profileBytes = await readKnownFile(rootDir, PROFILE_PATH);
18
+ if (!profileBytes.ok) {
19
+ issues.push(issue("LINT-STRUCT-001", "error", PROFILE_PATH, "readable profile file", "missing", "ai-profile.yaml was not found.", "Create ai-profile.yaml at the repository root before running doctor."));
20
+ return toResult(issues);
21
+ }
22
+ const profileResult = parseProfileYaml(decodeUtf8(profileBytes.bytes), { sourcePath: PROFILE_PATH });
23
+ if (!profileResult.ok) {
24
+ for (const profileIssue of profileResult.issues) issues.push(issue("LINT-STRUCT-002", "error", profileIssue.path, profileIssue.expected, profileIssue.actual, profileIssue.message, "Fix ai-profile.yaml before running compiler or doctor checks."));
25
+ return toResult(issues);
26
+ }
27
+ const compileResult = compileProfile({ profile: profileResult.profile });
28
+ if (!compileResult.ok) {
29
+ for (const compileIssue of compileResult.issues) issues.push(issue("LINT-STRUCT-002", "error", compileIssue.path, compileIssue.expected, compileIssue.actual, compileIssue.message, "Fix ai-profile.yaml client and target settings before running doctor."));
30
+ return toResult(issues);
31
+ }
32
+ await checkGeneratedArtifactsExist(rootDir, compileResult.files, issues);
33
+ await checkGeneratedArtifactSecurity(rootDir, compileResult.files, issues);
34
+ await checkSkillFiles(rootDir, issues);
35
+ await checkSemanticWarnings(rootDir, compileResult.files, issues);
36
+ await checkGitignoreSecretHygiene(rootDir, issues);
37
+ const lockfile = await readAndValidateLockfile(rootDir, issues);
38
+ if (lockfile) await checkLockfileDrift({
39
+ rootDir,
40
+ profileBytes: profileBytes.bytes,
41
+ templates: compileResult.templates,
42
+ files: compileResult.files,
43
+ lockfile,
44
+ issues
45
+ });
46
+ await checkPermissionPosture(rootDir, profileResult.profile, issues);
47
+ return toResult(issues);
48
+ }
49
+ async function checkGeneratedArtifactsExist(rootDir, files, issues) {
50
+ for (const file of files) if (!(await readKnownFile(rootDir, file.path)).ok) issues.push(issue("LINT-STRUCT-003", "error", file.path, `generated artifact for ${file.target}`, "missing", `${file.path} is missing for enabled target ${file.target}.`, "Run the compiler after reviewing enabled targets."));
51
+ }
52
+ async function checkGeneratedArtifactSecurity(rootDir, files, issues) {
53
+ for (const file of files) {
54
+ const current = await readKnownFile(rootDir, file.path);
55
+ if (!current.ok) continue;
56
+ const text = decodeUtf8(current.bytes);
57
+ if (containsSecretLikeLiteral(text)) issues.push(issue("LINT-SEC-001", "error", file.path, "no literal secret-like values", "secret-like value present", `${file.path} contains a secret-like literal.`, "Remove literal secrets and use environment variable names instead."));
58
+ if (containsLiteralEnvValue(file.path, text)) issues.push(issue("LINT-SEC-003", "error", file.path, "environment variable references only", "literal env value present", `${file.path} contains a literal environment value.`, "Use environment variable names such as $TOKEN instead of literal values."));
59
+ }
60
+ }
61
+ async function checkGitignoreSecretHygiene(rootDir, issues) {
62
+ const gitignore = await readKnownFile(rootDir, ".gitignore");
63
+ if (!gitignore.ok) {
64
+ issues.push(issue("LINT-SEC-002", "warning", ".gitignore", ".env and .env.* ignored", "missing .gitignore", ".gitignore is missing, so doctor cannot verify .env ignore hygiene.", "Add .env and .env.* to .gitignore before using generated agent config."));
65
+ return;
66
+ }
67
+ const lines = decodeUtf8(gitignore.bytes).split(/\r?\n/u).map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#"));
68
+ const hasEnv = lines.some((line) => line === ".env" || line === "/.env");
69
+ const hasEnvStar = lines.some((line) => line === ".env.*" || line === "/.env.*" || line === ".env*" || line === "/.env*");
70
+ if (!hasEnv || !hasEnvStar) issues.push(issue("LINT-SEC-002", "warning", ".gitignore", ".env and .env.* ignored", "incomplete ignore rules", ".gitignore does not clearly ignore .env and .env.* files.", "Add .env and .env.* ignore rules without printing or reading secret files."));
71
+ }
72
+ async function checkSkillFiles(rootDir, issues) {
73
+ const skillFiles = await collectSkillFiles(rootDir);
74
+ for (const skillPath of skillFiles) {
75
+ const file = await readKnownFile(rootDir, skillPath);
76
+ if (!file.ok) continue;
77
+ const text = decodeUtf8(file.bytes);
78
+ const lineCount = countLines(text);
79
+ if (lineCount > 500) issues.push(issue("LINT-SKILL-001", "error", skillPath, "500 lines or fewer", `${lineCount} lines`, `${skillPath} exceeds the hard skill size limit.`, "Split large skill content into references or narrower skills."));
80
+ else if (lineCount > 300) issues.push(issue("LINT-SKILL-001", "warning", skillPath, "300 lines or fewer recommended", `${lineCount} lines`, `${skillPath} exceeds the recommended skill size.`, "Consider moving detailed context into references or narrowing the skill."));
81
+ if (!hasSkillTrigger(text)) issues.push(issue("LINT-SKILL-002", "warning", skillPath, "clear trigger or use-case", "missing", `${skillPath} does not describe when the skill should be used.`, "Add concise \"Use when\", \"Use before\", or \"Triggers\" language to the skill."));
82
+ if (duplicatesGenericProjectFacts(text)) issues.push(issue("LINT-SKILL-003", "warning", skillPath, "task-specific skill content", "generic project facts duplicated", `${skillPath} appears to duplicate generic project facts.`, "Keep stack and project facts in generated project instructions rather than every skill."));
83
+ }
84
+ }
85
+ async function checkSemanticWarnings(rootDir, files, issues) {
86
+ for (const file of files) {
87
+ const current = await readKnownFile(rootDir, file.path);
88
+ if (!current.ok) continue;
89
+ const text = decodeUtf8(current.bytes);
90
+ if (countOccurrences(text, "Do not upload source code") > 1) issues.push(issue("LINT-SEM-001", "warning", file.path, "minimal repeated safety boilerplate", "repeated safety statement", `${file.path} repeats generated safety guidance.`, "Remove redundant repeated instructions or regenerate from ai-profile.yaml."));
91
+ if (containsContradictionMarker(text)) issues.push(issue("LINT-SEM-002", "warning", file.path, "no direct contradiction markers", "contradiction marker present", `${file.path} contains an obvious contradictory instruction marker.`, "Review manual edits and regenerate generated instructions if needed."));
92
+ }
93
+ }
94
+ async function readAndValidateLockfile(rootDir, issues) {
95
+ const lockfileBytes = await readKnownFile(rootDir, LOCKFILE_PATH);
96
+ if (!lockfileBytes.ok) {
97
+ issues.push(issue("LINT-LOCK-001", "error", LOCKFILE_PATH, "readable lockfile", "missing", "ai-profile.lock was not found.", "Run the compiler after reviewing changes so ai-profile.lock can be generated."));
98
+ return;
99
+ }
100
+ const validation = validateLockfileText(decodeUtf8(lockfileBytes.bytes));
101
+ if (!validation.ok) {
102
+ for (const lockIssue of validation.issues) issues.push(issue("LINT-LOCK-002", "error", lockIssue.path, lockIssue.expected, lockIssue.actual, lockIssue.message, "Regenerate ai-profile.lock after reviewing the lockfile issue."));
103
+ return;
104
+ }
105
+ return validation.lockfile;
106
+ }
107
+ async function checkLockfileDrift(input) {
108
+ const expectedLockfileText = createLockfileFile({
109
+ profileBytes: input.profileBytes,
110
+ templates: input.templates,
111
+ files: input.files
112
+ });
113
+ const expectedLockfile = JSON.parse(Buffer.from(expectedLockfileText.bytes).toString("utf8"));
114
+ const currentProfileHash = sha256Hex(input.profileBytes);
115
+ if (input.lockfile.profile.sha256 !== currentProfileHash) input.issues.push(issue("LINT-LOCK-003", "error", "/profile/sha256", currentProfileHash, input.lockfile.profile.sha256, "ai-profile.lock profile hash does not match ai-profile.yaml.", "Regenerate ai-profile.lock after reviewing profile changes."));
116
+ compareTemplates(input.lockfile.templates, expectedLockfile.templates, input.issues);
117
+ compareOutputs(input.lockfile.outputs, expectedLockfile.outputs, input.issues);
118
+ const lockOutputsByKey = new Map(input.lockfile.outputs.map((output) => [outputKey(output), output]));
119
+ for (const output of expectedLockfile.outputs) {
120
+ const expectedHash = lockOutputsByKey.get(outputKey(output))?.sha256 ?? output.sha256;
121
+ const current = await readKnownFile(input.rootDir, output.path);
122
+ if (!current.ok) {
123
+ input.issues.push(issue("LINT-LOCK-006", "error", output.path, "generated file present", "missing", `${output.path} is listed in ai-profile.lock but is missing.`, "Run the compiler after reviewing generated file changes."));
124
+ continue;
125
+ }
126
+ const currentHash = sha256Hex(current.bytes);
127
+ if (currentHash !== expectedHash) input.issues.push(issue("LINT-LOCK-007", "error", output.path, expectedHash, currentHash, `${output.path} bytes differ from ai-profile.lock.`, "Regenerate the file or lockfile after reviewing the generated diff."));
128
+ }
129
+ }
130
+ function compareTemplates(actual, expected, issues) {
131
+ const actualMap = new Map(actual.map((template) => [templateKey(template), template]));
132
+ const expectedMap = new Map(expected.map((template) => [templateKey(template), template]));
133
+ for (const [key, expectedTemplate] of expectedMap) {
134
+ const actualTemplate = actualMap.get(key);
135
+ if (!actualTemplate) {
136
+ issues.push(issue("LINT-LOCK-004", "error", `/templates/${expectedTemplate.id}`, "template recorded in lockfile", "missing", `${expectedTemplate.id} is missing from ai-profile.lock.`, "Regenerate ai-profile.lock after reviewing template changes."));
137
+ continue;
138
+ }
139
+ if (actualTemplate.version !== expectedTemplate.version || actualTemplate.sha256 !== expectedTemplate.sha256) issues.push(issue("LINT-LOCK-004", "error", `/templates/${expectedTemplate.id}`, `${expectedTemplate.version}/${expectedTemplate.sha256}`, `${actualTemplate.version}/${actualTemplate.sha256}`, `${expectedTemplate.id} template metadata differs from the current compiler.`, "Regenerate ai-profile.lock after reviewing template changes."));
140
+ }
141
+ for (const [key, actualTemplate] of actualMap) if (!expectedMap.has(key)) issues.push(issue("LINT-LOCK-004", "error", `/templates/${actualTemplate.id}`, "current compiler template", "extra lockfile template", `${actualTemplate.id} is not emitted by the current compiler request.`, "Regenerate ai-profile.lock after reviewing target changes."));
142
+ }
143
+ function compareOutputs(actual, expected, issues) {
144
+ const actualMap = new Map(actual.map((output) => [outputKey(output), output]));
145
+ const expectedMap = new Map(expected.map((output) => [outputKey(output), output]));
146
+ for (const [key, expectedOutput] of expectedMap) {
147
+ const actualOutput = actualMap.get(key);
148
+ if (!actualOutput) {
149
+ issues.push(issue("LINT-LOCK-005", "error", expectedOutput.path, "output recorded in lockfile", "missing", `${expectedOutput.path} is missing from ai-profile.lock outputs.`, "Regenerate ai-profile.lock after reviewing generated output changes."));
150
+ continue;
151
+ }
152
+ if (actualOutput.templateId !== expectedOutput.templateId || actualOutput.sha256 !== expectedOutput.sha256) issues.push(issue("LINT-LOCK-005", "error", expectedOutput.path, `${expectedOutput.templateId}/${expectedOutput.sha256}`, `${actualOutput.templateId}/${actualOutput.sha256}`, `${expectedOutput.path} lockfile output metadata differs from current compiler output.`, "Regenerate generated outputs and ai-profile.lock after reviewing changes."));
153
+ }
154
+ for (const [key, actualOutput] of actualMap) if (!expectedMap.has(key)) issues.push(issue("LINT-LOCK-005", "error", actualOutput.path, "current compiler output", "extra lockfile output", `${actualOutput.path} is not emitted by the current compiler request.`, "Regenerate ai-profile.lock after reviewing target changes."));
155
+ }
156
+ async function checkPermissionPosture(rootDir, profile, issues) {
157
+ const safety = normalizeSafety(profile);
158
+ const effective = deriveEffectivePermissions(profile);
159
+ const preset = deriveEffectivePermissions({ safety: profile.safety });
160
+ const autonomousSandbox = safety.mode === "autonomous" && safety.requiresSandbox;
161
+ if (safety.mode === "guarded" && effective.shell.run === "allow") issues.push(permissionIssue("LINT-PERM-001", "error", "/permissions/shell/run", "ask or deny", "allow", "Guarded mode cannot default shell execution to allow.", "Use shell.run: ask or shell.run: deny for guarded profiles."));
162
+ if (safety.mode === "guarded" && effective.dependencies.install === "allow") issues.push(permissionIssue("LINT-PERM-002", "error", "/permissions/dependencies/install", "ask or deny", "allow", "Guarded mode cannot default dependency installation to allow.", "Use dependencies.install: ask or dependencies.install: deny for guarded profiles."));
163
+ if (effective.secrets.access !== "deny" || effective.production.access !== "deny") issues.push(permissionIssue("LINT-PERM-003", "error", "/permissions", "secrets and production access deny", "looser access", "Secrets and production access must remain denied.", "Remove any profile or project setting that grants secrets or production access."));
164
+ if (safety.mode === "autonomous" && !safety.requiresSandbox) issues.push(permissionIssue("LINT-PERM-004", "error", "/safety/requiresSandbox", "true", "false", "Autonomous mode requires explicit sandbox intent.", "Set safety.requiresSandbox: true only for isolated environments."));
165
+ if (hasUnsafeAutoApproval(effective, safety.mode) && !autonomousSandbox) issues.push(permissionIssue("LINT-PERM-004", "error", "/permissions", "ask/deny or autonomous sandbox intent", "dangerous allow", "Dangerous tools cannot default to auto-approval without autonomous sandbox intent.", "Use ask/deny permissions or require sandboxed autonomous mode."));
166
+ for (const looser of getLooserOverrides(preset, effective)) issues.push(permissionIssue("LINT-PERM-005", "warning", looser.path, looser.expected, looser.actual, `${looser.path} is looser than the selected safety preset.`, "Review the explicit override or wait for an approved policy that allows it."));
167
+ const sandboxEvidence = await checkProjectPermissionConfig(rootDir, profile, effective, autonomousSandbox, issues);
168
+ if (autonomousSandbox && !sandboxEvidence) issues.push(permissionIssue("LINT-PERM-004", "warning", "/safety/requiresSandbox", "verifiable generated sandbox config", "not verifiable", "Autonomous mode declares sandbox intent, but doctor could not verify generated sandbox config.", "Ensure generated Codex or Claude project config enforces sandboxing before using autonomous mode."));
169
+ reportRuntimeUnverifiable(profile, issues);
170
+ }
171
+ async function checkProjectPermissionConfig(rootDir, profile, effective, autonomousSandbox, issues) {
172
+ const codexSandbox = profile.clients.codex.enabled ? await checkCodexConfig(rootDir, effective, autonomousSandbox, issues) : false;
173
+ const claudeSandbox = profile.clients.claude.enabled ? await checkClaudeConfig(rootDir, effective, autonomousSandbox, issues) : false;
174
+ return codexSandbox || claudeSandbox;
175
+ }
176
+ async function checkCodexConfig(rootDir, effective, autonomousSandbox, issues) {
177
+ const file = await readKnownFile(rootDir, ".codex/config.toml");
178
+ if (!file.ok) return false;
179
+ const config = parseSimpleToml(decodeUtf8(file.bytes));
180
+ const sandboxMode = config.values.get("sandbox_mode");
181
+ const approvalPolicy = config.values.get("approval_policy");
182
+ const networkAccess = config.values.get("sandbox_workspace_write.network_access");
183
+ const allowLoginShell = config.values.get("allow_login_shell");
184
+ const hasSandbox = sandboxMode === "workspace-write" || sandboxMode === "read-only";
185
+ if (config.unsupportedLines.length > 0) issues.push(permissionIssue("LINT-PERM-005", "warning", ".codex/config.toml", "supported scalar TOML syntax", "unsupported TOML syntax", ".codex/config.toml contains syntax doctor cannot fully evaluate.", "Keep safety-relevant Codex settings on scalar key/value lines or extend doctor parser coverage before relying on this result."));
186
+ if (sandboxMode === "danger-full-access") issues.push(permissionIssue("LINT-PERM-004", "error", ".codex/config.toml", "sandboxed mode", "danger-full-access", "Codex danger-full-access is never allowed in generated or project config.", "Use workspace-write or read-only sandbox mode."));
187
+ if (approvalPolicy === "never" && !autonomousSandbox) issues.push(permissionIssue("LINT-PERM-004", "error", ".codex/config.toml", "interactive approval policy", "never", "Codex approval_policy = never is dangerous without autonomous sandbox intent.", "Use on-request for guarded or balanced project config."));
188
+ if (approvalPolicy === "on-failure") issues.push(permissionIssue("LINT-PERM-005", "warning", ".codex/config.toml", "current approval policy", "on-failure", "Codex approval_policy = on-failure is deprecated.", "Use on-request for interactive runs."));
189
+ if (allowLoginShell === true && effective.shell.run !== "allow") issues.push(configLooserIssue(".codex/config.toml", "non-login shell execution", "allow_login_shell = true"));
190
+ if (networkAccess === true && effective.network.external !== "allow") issues.push(configLooserIssue(".codex/config.toml", "network ask/deny", "network_access = true"));
191
+ if (sandboxMode === "workspace-write" && effective.filesystem.write === "deny") issues.push(configLooserIssue(".codex/config.toml", "read-only filesystem posture", "workspace-write"));
192
+ return hasSandbox;
193
+ }
194
+ async function checkClaudeConfig(rootDir, effective, autonomousSandbox, issues) {
195
+ const merged = mergeClaudeSettings(await readJsonObject(rootDir, ".claude/settings.json", issues), await readJsonObject(rootDir, ".claude/settings.local.json", issues));
196
+ if (!merged) return false;
197
+ const permissions = getRecord(merged.permissions);
198
+ const sandbox = getRecord(merged.sandbox);
199
+ const defaultMode = permissions ? permissions.defaultMode : void 0;
200
+ const disableBypass = permissions ? permissions.disableBypassPermissionsMode : void 0;
201
+ const disableAuto = permissions ? permissions.disableAutoMode : void 0;
202
+ if (defaultMode === "bypassPermissions") issues.push(permissionIssue("LINT-PERM-004", "error", ".claude/settings.json", "non-bypass permission mode", "bypassPermissions", "Claude bypassPermissions cannot be a generated or project default.", "Use default or plan mode and keep bypass mode disabled."));
203
+ if (defaultMode === "auto" && !autonomousSandbox) issues.push(permissionIssue("LINT-PERM-004", "error", ".claude/settings.json", "manual approval mode", "auto", "Claude auto mode is too loose without autonomous sandbox intent.", "Use default mode for guarded or balanced profiles."));
204
+ if (!autonomousSandbox && disableBypass !== "disable") issues.push(permissionIssue("LINT-PERM-004", "error", ".claude/settings.json", "disableBypassPermissionsMode = \"disable\"", describeSetting(disableBypass), "Claude bypass mode guard is missing or not disabled.", "Set permissions.disableBypassPermissionsMode to \"disable\"."));
205
+ if (!autonomousSandbox && disableAuto !== "disable") issues.push(permissionIssue("LINT-PERM-004", "error", ".claude/settings.json", "disableAutoMode = \"disable\"", describeSetting(disableAuto), "Claude auto mode guard is missing or not disabled.", "Set permissions.disableAutoMode to \"disable\"."));
206
+ const allowRules = getStringArray(permissions?.allow);
207
+ const askRules = getStringArray(permissions?.ask);
208
+ const effectiveRuleSurface = evaluateClaudeRules(getStringArray(permissions?.deny), askRules, allowRules);
209
+ if (effectiveRuleSurface.bash === "allow" && effective.shell.run !== "allow") issues.push(configLooserIssue(".claude/settings.json", "Bash ask/deny", "Bash allow rule"));
210
+ if (effectiveRuleSurface.edit === "allow" && effective.filesystem.write !== "allow") issues.push(configLooserIssue(".claude/settings.json", "file edits ask/deny", "Edit/Write allow rule"));
211
+ if (effectiveRuleSurface.webFetch === "allow" && effective.network.external !== "allow") issues.push(configLooserIssue(".claude/settings.json", "WebFetch ask/deny", "WebFetch allow rule"));
212
+ if (sandbox) {
213
+ if (sandbox.autoAllowBashIfSandboxed === true && effective.shell.run !== "allow") issues.push(configLooserIssue(".claude/settings.json", "Bash ask/deny", "autoAllowBashIfSandboxed = true"));
214
+ if (sandbox.enableWeakerNestedSandbox === true || sandbox.enableWeakerNetworkIsolation === true) issues.push(configLooserIssue(".claude/settings.json", "strong sandbox isolation", "weaker sandbox flag"));
215
+ const filesystem = getRecord(sandbox.filesystem);
216
+ const network = getRecord(sandbox.network);
217
+ if (filesystem && getStringArray(filesystem.allowWrite).length > 0 && effective.filesystem.write !== "allow") issues.push(configLooserIssue(".claude/settings.json", "file writes ask/deny", "sandbox.filesystem.allowWrite"));
218
+ if (network) {
219
+ if ((getStringArray(network.allowedDomains).length > 0 || network.allowAllUnixSockets === true || network.allowLocalBinding === true || network.allowMachLookup === true || typeof network.httpProxyPort === "number" || typeof network.socksProxyPort === "number") && effective.network.external !== "allow") issues.push(configLooserIssue(".claude/settings.json", "network ask/deny", "broad sandbox.network setting"));
220
+ }
221
+ }
222
+ return getRecord(merged.sandbox)?.enabled === true;
223
+ }
224
+ async function readJsonObject(rootDir, relativePath, issues) {
225
+ const file = await readKnownFile(rootDir, relativePath);
226
+ if (!file.ok) return;
227
+ try {
228
+ return getRecord(JSON.parse(decodeUtf8(file.bytes)));
229
+ } catch {
230
+ issues.push(permissionIssue("LINT-PERM-005", "warning", relativePath, "valid JSON", "parse error", `${relativePath} could not be parsed for permission checks.`, "Fix the JSON before relying on doctor permission results."));
231
+ return;
232
+ }
233
+ }
234
+ function mergeClaudeSettings(project, local) {
235
+ if (!project) return local;
236
+ if (!local) return project;
237
+ return mergeRecords(project, local);
238
+ }
239
+ function mergeRecords(left, right) {
240
+ const result = { ...left };
241
+ for (const [key, value] of Object.entries(right)) {
242
+ const previous = result[key];
243
+ if (Array.isArray(previous) && Array.isArray(value)) result[key] = Array.from(new Set([...previous, ...value]));
244
+ else if (getRecord(previous) && getRecord(value)) result[key] = mergeRecords(previous, value);
245
+ else result[key] = value;
246
+ }
247
+ return result;
248
+ }
249
+ function evaluateClaudeRules(denyRules, askRules, allowRules) {
250
+ return {
251
+ bash: evaluateClaudeTool("Bash", denyRules, askRules, allowRules),
252
+ edit: looserMode(evaluateClaudeTool("Edit", denyRules, askRules, allowRules), evaluateClaudeTool("Write", denyRules, askRules, allowRules)),
253
+ webFetch: evaluateClaudeTool("WebFetch", denyRules, askRules, allowRules)
254
+ };
255
+ }
256
+ function evaluateClaudeTool(tool, denyRules, askRules, allowRules) {
257
+ if (denyRules.some((rule) => isBareClaudeToolRule(rule, tool))) return "deny";
258
+ if (askRules.some((rule) => isBareClaudeToolRule(rule, tool))) return "ask";
259
+ if (allowRules.some((rule) => matchesClaudeTool(rule, tool))) return "allow";
260
+ if (askRules.some((rule) => matchesClaudeTool(rule, tool))) return "ask";
261
+ return "ask";
262
+ }
263
+ function matchesClaudeTool(rule, tool) {
264
+ return rule === tool || rule.startsWith(`${tool}(`);
265
+ }
266
+ function isBareClaudeToolRule(rule, tool) {
267
+ return rule === tool;
268
+ }
269
+ function parseSimpleToml(source) {
270
+ const values = /* @__PURE__ */ new Map();
271
+ const unsupportedLines = [];
272
+ let table = "";
273
+ source.split(/\r?\n/u).forEach((rawLine, index) => {
274
+ const line = stripTomlInlineComment(rawLine).trim();
275
+ if (line === "" || line.startsWith("#")) return;
276
+ const tableMatch = /^\[([A-Za-z0-9_.-]+)\]$/u.exec(line);
277
+ if (tableMatch) {
278
+ table = tableMatch[1] ?? "";
279
+ return;
280
+ }
281
+ const assignment = /^([A-Za-z0-9_.-]+)\s*=\s*(.+)$/u.exec(line);
282
+ if (!assignment) {
283
+ unsupportedLines.push(index + 1);
284
+ return;
285
+ }
286
+ const key = table ? `${table}.${assignment[1]}` : assignment[1];
287
+ const scalar = parseTomlScalar(assignment[2] ?? "");
288
+ if (scalar.ok) values.set(key, scalar.value);
289
+ else unsupportedLines.push(index + 1);
290
+ });
291
+ return {
292
+ values,
293
+ unsupportedLines
294
+ };
295
+ }
296
+ function parseTomlScalar(value) {
297
+ const trimmed = value.trim();
298
+ if (trimmed === "true") return {
299
+ ok: true,
300
+ value: true
301
+ };
302
+ if (trimmed === "false") return {
303
+ ok: true,
304
+ value: false
305
+ };
306
+ if (/^-?\d+$/u.test(trimmed)) return {
307
+ ok: true,
308
+ value: Number(trimmed)
309
+ };
310
+ const stringMatch = /^"([^"]*)"$/u.exec(trimmed);
311
+ if (stringMatch) return {
312
+ ok: true,
313
+ value: stringMatch[1] ?? ""
314
+ };
315
+ return { ok: false };
316
+ }
317
+ function stripTomlInlineComment(line) {
318
+ let inString = false;
319
+ let escaped = false;
320
+ for (let index = 0; index < line.length; index += 1) {
321
+ const character = line[index];
322
+ if (character === "\\" && inString) {
323
+ escaped = !escaped;
324
+ continue;
325
+ }
326
+ if (character === "\"" && !escaped) inString = !inString;
327
+ if (character === "#" && !inString) return line.slice(0, index);
328
+ escaped = false;
329
+ }
330
+ return line;
331
+ }
332
+ function hasUnsafeAutoApproval(effective, safetyMode) {
333
+ return effective.shell.run === "allow" || effective.dependencies.install === "allow" || effective.network.external === "allow" || safetyMode !== "balanced" && effective.filesystem.write === "allow";
334
+ }
335
+ function getLooserOverrides(preset, effective) {
336
+ return [
337
+ {
338
+ path: "/permissions/filesystem/read",
339
+ expected: preset.filesystem.read,
340
+ actual: effective.filesystem.read
341
+ },
342
+ {
343
+ path: "/permissions/filesystem/write",
344
+ expected: preset.filesystem.write,
345
+ actual: effective.filesystem.write
346
+ },
347
+ {
348
+ path: "/permissions/shell/run",
349
+ expected: preset.shell.run,
350
+ actual: effective.shell.run
351
+ },
352
+ {
353
+ path: "/permissions/dependencies/install",
354
+ expected: preset.dependencies.install,
355
+ actual: effective.dependencies.install
356
+ },
357
+ {
358
+ path: "/permissions/network/external",
359
+ expected: preset.network.external,
360
+ actual: effective.network.external
361
+ }
362
+ ].filter((check) => isLooser(check.actual, check.expected));
363
+ }
364
+ function isLooser(actual, expected) {
365
+ return permissionRank(actual) > permissionRank(expected);
366
+ }
367
+ function looserMode(left, right) {
368
+ return permissionRank(left) >= permissionRank(right) ? left : right;
369
+ }
370
+ function permissionRank(mode) {
371
+ switch (mode) {
372
+ case "deny": return 0;
373
+ case "ask": return 1;
374
+ case "allow": return 2;
375
+ }
376
+ }
377
+ function reportRuntimeUnverifiable(profile, issues) {
378
+ if (profile.clients.tabnine.enabled) issues.push(permissionIssue("LINT-PERM-006", "info", "tabnine-runtime", "verified IDE Tool Permissions state", "not verifiable", "Tabnine IDE runtime tool permission state cannot be verified from project files.", "Manually verify Tool Permissions in Tabnine IDE settings: Auto-approve, Ask first, or Disable."));
379
+ if (profile.clients.codex.enabled) issues.push(permissionIssue("LINT-PERM-006", "info", "codex-runtime", "verified runtime approval and sandbox flags", "not verifiable", "Codex runtime flags or user configuration may override project config.", "Verify the active Codex approval policy and sandbox mode before trusting runtime posture."));
380
+ if (profile.clients.claude.enabled) issues.push(permissionIssue("LINT-PERM-006", "info", "claude-runtime", "verified CLI permission mode and merged user settings", "not verifiable", "Claude CLI flags and user settings may override project settings.", "Verify the active Claude permission mode and settings scopes before trusting runtime posture."));
381
+ }
382
+ async function collectSkillFiles(rootDir) {
383
+ const files = [];
384
+ for (const skillRoot of SKILL_ROOTS) await collectSkillFilesUnder(rootDir, skillRoot, files);
385
+ return files.sort();
386
+ }
387
+ async function collectSkillFilesUnder(rootDir, relativeRoot, files) {
388
+ let entries;
389
+ try {
390
+ entries = await readdir(path.join(rootDir, relativeRoot), { withFileTypes: true });
391
+ } catch (error) {
392
+ if (isNodeError(error) && error.code === "ENOENT") return;
393
+ throw error;
394
+ }
395
+ for (const entry of entries) {
396
+ const relativePath = `${relativeRoot}/${entry.name}`;
397
+ if (entry.isDirectory()) await collectSkillFilesUnder(rootDir, relativePath, files);
398
+ else if (entry.isFile() && entry.name === "SKILL.md") files.push(relativePath);
399
+ }
400
+ }
401
+ async function readKnownFile(rootDir, relativePath) {
402
+ const safePath = safeOutputPath(relativePath);
403
+ if (safePath === ".env" || safePath.startsWith(".env.")) return { ok: false };
404
+ try {
405
+ return {
406
+ ok: true,
407
+ bytes: await readFile(path.join(rootDir, safePath))
408
+ };
409
+ } catch (error) {
410
+ if (isNodeError(error) && error.code === "ENOENT") return { ok: false };
411
+ throw error;
412
+ }
413
+ }
414
+ function templateKey(template) {
415
+ return `${template.id}\u0000${template.target}`;
416
+ }
417
+ function outputKey(output) {
418
+ return `${output.path}\u0000${output.target}`;
419
+ }
420
+ function configLooserIssue(pathValue, expected, actual) {
421
+ return permissionIssue("LINT-PERM-005", "warning", pathValue, expected, actual, `${pathValue} is looser than effectivePermissions.`, "Review project config or regenerate it from ai-profile.yaml.");
422
+ }
423
+ function permissionIssue(code, severity, pathValue, expected, actual, message, guidance) {
424
+ return issue(code, severity, pathValue, expected, actual, message, guidance);
425
+ }
426
+ function issue(code, severity, pathValue, expected, actual, message, guidance) {
427
+ return {
428
+ code,
429
+ severity,
430
+ path: pathValue,
431
+ expected,
432
+ actual,
433
+ message,
434
+ guidance
435
+ };
436
+ }
437
+ function toResult(issues) {
438
+ const sortedIssues = issues.sort(compareDoctorIssues);
439
+ const status = getStatus(sortedIssues);
440
+ return {
441
+ ok: status !== "fail",
442
+ status,
443
+ issues: sortedIssues
444
+ };
445
+ }
446
+ function getStatus(issues) {
447
+ if (issues.some((item) => item.severity === "error")) return "fail";
448
+ if (issues.some((item) => item.severity === "warning")) return "warn";
449
+ return "pass";
450
+ }
451
+ function compareDoctorIssues(left, right) {
452
+ return SEVERITY_ORDER[left.severity] - SEVERITY_ORDER[right.severity] || compareText(left.path, right.path) || compareText(left.code, right.code) || compareText(left.message, right.message);
453
+ }
454
+ function compareText(left, right) {
455
+ if (left < right) return -1;
456
+ if (left > right) return 1;
457
+ return 0;
458
+ }
459
+ function getRecord(value) {
460
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return value;
461
+ }
462
+ function getStringArray(value) {
463
+ if (!Array.isArray(value)) return [];
464
+ return value.filter((item) => typeof item === "string");
465
+ }
466
+ function describeSetting(value) {
467
+ if (typeof value === "string") return value;
468
+ if (value === void 0) return "missing";
469
+ if (value === null) return "null";
470
+ if (Array.isArray(value)) return "array";
471
+ return typeof value;
472
+ }
473
+ function containsLiteralEnvValue(pathValue, text) {
474
+ if (pathValue.endsWith(".json")) try {
475
+ return hasLiteralEnvObjectValue(JSON.parse(text));
476
+ } catch {
477
+ return false;
478
+ }
479
+ if (pathValue.endsWith(".toml")) return /^\s*(?:env|env_vars|env_http_headers)\s*=\s*"(?!\$|\$\{)[^"]+"/imu.test(text);
480
+ return false;
481
+ }
482
+ function hasLiteralEnvObjectValue(value) {
483
+ if (Array.isArray(value)) return value.some(hasLiteralEnvObjectValue);
484
+ const record = getRecord(value);
485
+ if (!record) return false;
486
+ for (const [key, item] of Object.entries(record)) {
487
+ const envRecord = getRecord(item);
488
+ if (key === "env" && envRecord) {
489
+ for (const envValue of Object.values(envRecord)) if (typeof envValue === "string" && envValue !== "" && !envValue.startsWith("$") && !envValue.startsWith("${")) return true;
490
+ }
491
+ if (hasLiteralEnvObjectValue(item)) return true;
492
+ }
493
+ return false;
494
+ }
495
+ function countLines(text) {
496
+ if (text === "") return 0;
497
+ return text.replace(/\r\n?/gu, "\n").split("\n").length;
498
+ }
499
+ function hasSkillTrigger(text) {
500
+ return /(^description:\s+\S|\buse\b.{0,120}\b(when|before)\b|\btriggers?\b)/imsu.test(text);
501
+ }
502
+ function duplicatesGenericProjectFacts(text) {
503
+ return /\b(Languages|Frameworks|Package managers|Testing):/u.test(text);
504
+ }
505
+ function countOccurrences(text, pattern) {
506
+ let count = 0;
507
+ let index = text.indexOf(pattern);
508
+ while (index !== -1) {
509
+ count += 1;
510
+ index = text.indexOf(pattern, index + pattern.length);
511
+ }
512
+ return count;
513
+ }
514
+ function containsContradictionMarker(text) {
515
+ return [
516
+ /ignore\s+AGENTS\.md/iu,
517
+ /ignore\s+ai-profile\.yaml/iu,
518
+ /you\s+may\s+upload\s+source\s+code/iu,
519
+ /upload\s+source\s+code\s+to/iu,
520
+ /skip\s+final\s+review/iu,
521
+ /disable\s+final\s+review/iu
522
+ ].some((pattern) => pattern.test(text));
523
+ }
524
+ function decodeUtf8(bytes) {
525
+ return Buffer.from(bytes).toString("utf8");
526
+ }
527
+ function isNodeError(error) {
528
+ return error instanceof Error && "code" in error;
529
+ }
530
+
531
+ export { runDoctor as r };
532
+ //# sourceMappingURL=dist--t-UlYlZ.js.map