@nanocollective/sentinel 0.1.0-alpha.0

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 (163) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +66 -0
  3. package/dist/cli.d.ts +12 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +254 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/parse.d.ts +13 -0
  8. package/dist/config/parse.d.ts.map +1 -0
  9. package/dist/config/parse.js +271 -0
  10. package/dist/config/parse.js.map +1 -0
  11. package/dist/config/repo-override.d.ts +13 -0
  12. package/dist/config/repo-override.d.ts.map +1 -0
  13. package/dist/config/repo-override.js +128 -0
  14. package/dist/config/repo-override.js.map +1 -0
  15. package/dist/config/types.d.ts +86 -0
  16. package/dist/config/types.d.ts.map +1 -0
  17. package/dist/config/types.js +8 -0
  18. package/dist/config/types.js.map +1 -0
  19. package/dist/dedup/hash.d.ts +10 -0
  20. package/dist/dedup/hash.d.ts.map +1 -0
  21. package/dist/dedup/hash.js +18 -0
  22. package/dist/dedup/hash.js.map +1 -0
  23. package/dist/dedup/markers.d.ts +13 -0
  24. package/dist/dedup/markers.d.ts.map +1 -0
  25. package/dist/dedup/markers.js +42 -0
  26. package/dist/dedup/markers.js.map +1 -0
  27. package/dist/dedup/plan.d.ts +45 -0
  28. package/dist/dedup/plan.d.ts.map +1 -0
  29. package/dist/dedup/plan.js +83 -0
  30. package/dist/dedup/plan.js.map +1 -0
  31. package/dist/dedup/reconcile.d.ts +29 -0
  32. package/dist/dedup/reconcile.d.ts.map +1 -0
  33. package/dist/dedup/reconcile.js +73 -0
  34. package/dist/dedup/reconcile.js.map +1 -0
  35. package/dist/findings/types.d.ts +53 -0
  36. package/dist/findings/types.d.ts.map +1 -0
  37. package/dist/findings/types.js +28 -0
  38. package/dist/findings/types.js.map +1 -0
  39. package/dist/findings/validate.d.ts +36 -0
  40. package/dist/findings/validate.d.ts.map +1 -0
  41. package/dist/findings/validate.js +178 -0
  42. package/dist/findings/validate.js.map +1 -0
  43. package/dist/index.d.ts +48 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +39 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/init/args.d.ts +20 -0
  48. package/dist/init/args.d.ts.map +1 -0
  49. package/dist/init/args.js +80 -0
  50. package/dist/init/args.js.map +1 -0
  51. package/dist/init/plan.d.ts +9 -0
  52. package/dist/init/plan.d.ts.map +1 -0
  53. package/dist/init/plan.js +17 -0
  54. package/dist/init/plan.js.map +1 -0
  55. package/dist/init/scaffold.d.ts +17 -0
  56. package/dist/init/scaffold.d.ts.map +1 -0
  57. package/dist/init/scaffold.js +27 -0
  58. package/dist/init/scaffold.js.map +1 -0
  59. package/dist/init/templates.d.ts +28 -0
  60. package/dist/init/templates.d.ts.map +1 -0
  61. package/dist/init/templates.js +187 -0
  62. package/dist/init/templates.js.map +1 -0
  63. package/dist/init/types.d.ts +29 -0
  64. package/dist/init/types.d.ts.map +1 -0
  65. package/dist/init/types.js +15 -0
  66. package/dist/init/types.js.map +1 -0
  67. package/dist/issues/body.d.ts +12 -0
  68. package/dist/issues/body.d.ts.map +1 -0
  69. package/dist/issues/body.js +53 -0
  70. package/dist/issues/body.js.map +1 -0
  71. package/dist/issues/file.d.ts +24 -0
  72. package/dist/issues/file.d.ts.map +1 -0
  73. package/dist/issues/file.js +61 -0
  74. package/dist/issues/file.js.map +1 -0
  75. package/dist/issues/gh-client.d.ts +21 -0
  76. package/dist/issues/gh-client.d.ts.map +1 -0
  77. package/dist/issues/gh-client.js +133 -0
  78. package/dist/issues/gh-client.js.map +1 -0
  79. package/dist/issues/types.d.ts +85 -0
  80. package/dist/issues/types.d.ts.map +1 -0
  81. package/dist/issues/types.js +8 -0
  82. package/dist/issues/types.js.map +1 -0
  83. package/dist/orchestrator/audit.d.ts +14 -0
  84. package/dist/orchestrator/audit.d.ts.map +1 -0
  85. package/dist/orchestrator/audit.js +47 -0
  86. package/dist/orchestrator/audit.js.map +1 -0
  87. package/dist/orchestrator/auto-fix.d.ts +31 -0
  88. package/dist/orchestrator/auto-fix.d.ts.map +1 -0
  89. package/dist/orchestrator/auto-fix.js +59 -0
  90. package/dist/orchestrator/auto-fix.js.map +1 -0
  91. package/dist/orchestrator/extract.d.ts +14 -0
  92. package/dist/orchestrator/extract.d.ts.map +1 -0
  93. package/dist/orchestrator/extract.js +70 -0
  94. package/dist/orchestrator/extract.js.map +1 -0
  95. package/dist/orchestrator/nanocoder-runner.d.ts +33 -0
  96. package/dist/orchestrator/nanocoder-runner.d.ts.map +1 -0
  97. package/dist/orchestrator/nanocoder-runner.js +125 -0
  98. package/dist/orchestrator/nanocoder-runner.js.map +1 -0
  99. package/dist/orchestrator/types.d.ts +51 -0
  100. package/dist/orchestrator/types.d.ts.map +1 -0
  101. package/dist/orchestrator/types.js +8 -0
  102. package/dist/orchestrator/types.js.map +1 -0
  103. package/dist/prompt/build.d.ts +14 -0
  104. package/dist/prompt/build.d.ts.map +1 -0
  105. package/dist/prompt/build.js +123 -0
  106. package/dist/prompt/build.js.map +1 -0
  107. package/dist/prompt/types.d.ts +34 -0
  108. package/dist/prompt/types.d.ts.map +1 -0
  109. package/dist/prompt/types.js +8 -0
  110. package/dist/prompt/types.js.map +1 -0
  111. package/dist/rule-packs/dependencies.d.ts +20 -0
  112. package/dist/rule-packs/dependencies.d.ts.map +1 -0
  113. package/dist/rule-packs/dependencies.js +58 -0
  114. package/dist/rule-packs/dependencies.js.map +1 -0
  115. package/dist/rule-packs/glob.d.ts +23 -0
  116. package/dist/rule-packs/glob.d.ts.map +1 -0
  117. package/dist/rule-packs/glob.js +66 -0
  118. package/dist/rule-packs/glob.js.map +1 -0
  119. package/dist/rule-packs/parse.d.ts +21 -0
  120. package/dist/rule-packs/parse.d.ts.map +1 -0
  121. package/dist/rule-packs/parse.js +230 -0
  122. package/dist/rule-packs/parse.js.map +1 -0
  123. package/dist/rule-packs/types.d.ts +55 -0
  124. package/dist/rule-packs/types.d.ts.map +1 -0
  125. package/dist/rule-packs/types.js +10 -0
  126. package/dist/rule-packs/types.js.map +1 -0
  127. package/dist/run/audit.d.ts +20 -0
  128. package/dist/run/audit.d.ts.map +1 -0
  129. package/dist/run/audit.js +28 -0
  130. package/dist/run/audit.js.map +1 -0
  131. package/dist/run/clone.d.ts +23 -0
  132. package/dist/run/clone.d.ts.map +1 -0
  133. package/dist/run/clone.js +51 -0
  134. package/dist/run/clone.js.map +1 -0
  135. package/dist/run/preview.d.ts +34 -0
  136. package/dist/run/preview.d.ts.map +1 -0
  137. package/dist/run/preview.js +68 -0
  138. package/dist/run/preview.js.map +1 -0
  139. package/dist/run/report.d.ts +10 -0
  140. package/dist/run/report.d.ts.map +1 -0
  141. package/dist/run/report.js +73 -0
  142. package/dist/run/report.js.map +1 -0
  143. package/dist/run/run.d.ts +74 -0
  144. package/dist/run/run.d.ts.map +1 -0
  145. package/dist/run/run.js +131 -0
  146. package/dist/run/run.js.map +1 -0
  147. package/dist/run/select.d.ts +14 -0
  148. package/dist/run/select.d.ts.map +1 -0
  149. package/dist/run/select.js +31 -0
  150. package/dist/run/select.js.map +1 -0
  151. package/dist/run/sources.d.ts +11 -0
  152. package/dist/run/sources.d.ts.map +1 -0
  153. package/dist/run/sources.js +106 -0
  154. package/dist/run/sources.js.map +1 -0
  155. package/dist/run/types.d.ts +55 -0
  156. package/dist/run/types.d.ts.map +1 -0
  157. package/dist/run/types.js +7 -0
  158. package/dist/run/types.js.map +1 -0
  159. package/dist/suppression/apply.d.ts +33 -0
  160. package/dist/suppression/apply.d.ts.map +1 -0
  161. package/dist/suppression/apply.js +55 -0
  162. package/dist/suppression/apply.js.map +1 -0
  163. package/package.json +118 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The rule pack format. A pack is a single file: a YAML manifest header
3
+ * followed by a Markdown body that is the audit prompt. The manifest is a
4
+ * stable v1 contract (see docs/rule-packs/index.md#manifest-fields).
5
+ *
6
+ * Manifest keys are snake_case on disk (`applies_to`, `severity_weighting`,
7
+ * `depends_on`) and normalised to camelCase here.
8
+ */
9
+ import type { Severity } from '../findings/types.js';
10
+ /** Scopes which files within a repository a pack reads. */
11
+ export interface AppliesTo {
12
+ /** Glob patterns for files the pack applies to. Empty = the whole repo. */
13
+ paths: string[];
14
+ /** Language identifiers the pack targets. A hint for scoping and reporting. */
15
+ languages: string[];
16
+ }
17
+ /** The validated, normalised manifest of a rule pack. */
18
+ export interface RulePackManifest {
19
+ /** Unique, kebab-case pack identifier. */
20
+ name: string;
21
+ /** Semver. Bumped whenever the audit body changes. */
22
+ version: string;
23
+ /** One-line description of what the pack audits for. */
24
+ description: string;
25
+ /** File scoping. */
26
+ appliesTo: AppliesTo;
27
+ /** Per-finding-type severity overrides, keyed by finding type. */
28
+ severityWeighting: Record<string, Severity>;
29
+ /** Other packs that should run alongside this one, by name. */
30
+ dependsOn: string[];
31
+ /** The pack's primary category, carried onto findings. */
32
+ category: string;
33
+ }
34
+ /** A parsed rule pack: its manifest and the Markdown audit prompt. */
35
+ export interface RulePack {
36
+ manifest: RulePackManifest;
37
+ /** The Markdown body handed to the model as the audit prompt. */
38
+ body: string;
39
+ }
40
+ /** A single reason a pack (or a manifest field) failed to parse or validate. */
41
+ export interface RulePackError {
42
+ /** The offending manifest field, or 'document' for structural errors. */
43
+ field: string;
44
+ /** A human-readable explanation. */
45
+ message: string;
46
+ }
47
+ /** The outcome of parsing a single rule pack file. */
48
+ export interface ParseResult {
49
+ valid: boolean;
50
+ /** The parsed pack, or null when validation failed. */
51
+ pack: RulePack | null;
52
+ /** Every reason parsing failed. Empty when valid. */
53
+ errors: RulePackError[];
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/rule-packs/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAEnD,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACzB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,yDAAyD;AACzD,MAAM,WAAW,gBAAgB;IAChC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,kEAAkE;IAClE,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,+DAA+D;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;CACb;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC7B,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,qDAAqD;IACrD,MAAM,EAAE,aAAa,EAAE,CAAC;CACxB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The rule pack format. A pack is a single file: a YAML manifest header
3
+ * followed by a Markdown body that is the audit prompt. The manifest is a
4
+ * stable v1 contract (see docs/rule-packs/index.md#manifest-fields).
5
+ *
6
+ * Manifest keys are snake_case on disk (`applies_to`, `severity_weighting`,
7
+ * `depends_on`) and normalised to camelCase here.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../source/rule-packs/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Run one rule pack against one repository's gathered files: build the prompt,
3
+ * run the audit with the auto-fix loop, and map the result into a PackOutcome.
4
+ */
5
+ import type { ModelConfig } from '../config/types.js';
6
+ import { type AutoFixOptions } from '../orchestrator/auto-fix.js';
7
+ import type { ModelRunner } from '../orchestrator/types.js';
8
+ import type { SourceFile } from '../prompt/types.js';
9
+ import type { RulePack } from '../rule-packs/types.js';
10
+ import type { PackOutcome } from './types.js';
11
+ /** The repository material one pack pass audits. */
12
+ export interface PackAuditContext {
13
+ repoName?: string;
14
+ files: SourceFile[];
15
+ context?: SourceFile[];
16
+ repoNotes?: string;
17
+ }
18
+ /** Audit one pack against one repo's files. */
19
+ export declare function auditPack(pack: RulePack, context: PackAuditContext, model: ModelConfig, runner: ModelRunner, options?: AutoFixOptions): Promise<PackOutcome>;
20
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../source/run/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACN,KAAK,cAAc,EAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,YAAY,CAAC;AAE5C,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,wBAAsB,SAAS,CAC9B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAqBtB"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Run one rule pack against one repository's gathered files: build the prompt,
3
+ * run the audit with the auto-fix loop, and map the result into a PackOutcome.
4
+ */
5
+ import { runAuditWithAutoFix, } from '../orchestrator/auto-fix.js';
6
+ import { buildAuditPrompt } from '../prompt/build.js';
7
+ /** Audit one pack against one repo's files. */
8
+ export async function auditPack(pack, context, model, runner, options = {}) {
9
+ const { prompt } = buildAuditPrompt({
10
+ pack,
11
+ files: context.files,
12
+ repoName: context.repoName,
13
+ context: context.context,
14
+ repoNotes: context.repoNotes,
15
+ });
16
+ const result = await runAuditWithAutoFix(prompt, model, runner, options);
17
+ return {
18
+ pack: pack.manifest.name,
19
+ version: pack.manifest.version,
20
+ findings: result.findings,
21
+ attempts: result.attempts,
22
+ ok: result.ok,
23
+ errors: result.errors,
24
+ runError: result.runError,
25
+ raw: result.raw,
26
+ };
27
+ }
28
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../source/run/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,mBAAmB,GACnB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAapD,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,SAAS,CAC9B,IAAc,EACd,OAAyB,EACzB,KAAkB,EAClB,MAAmB,EACnB,UAA0B,EAAE;IAE5B,MAAM,EAAC,MAAM,EAAC,GAAG,gBAAgB,CAAC;QACjC,IAAI;QACJ,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC5B,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAEzE,OAAO;QACN,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;QACxB,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO;QAC9B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,EAAE,MAAM,CAAC,GAAG;KACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Clone target repositories into the run workspace. The audited repos are named
3
+ * in sentinel.yaml, not known to the workflow statically, so `sentinel run`
4
+ * clones any that are not already present. Arg building is pure and tested; the
5
+ * gh spawn and fs checks are not.
6
+ */
7
+ /** Build the `gh repo clone` argv for a shallow single-branch clone. */
8
+ export declare function buildCloneArgs(repo: string, dir: string): string[];
9
+ /** The outcome of ensuring the target repos are present in the workspace. */
10
+ export interface EnsureTargetsResult {
11
+ cloned: string[];
12
+ skipped: string[];
13
+ errors: {
14
+ repo: string;
15
+ error: string;
16
+ }[];
17
+ }
18
+ /**
19
+ * Ensure each repo is checked out under `workspaceDir/<owner>/<name>`, cloning
20
+ * any that are missing. Repos already present are left untouched.
21
+ */
22
+ export declare function ensureTargets(repos: string[], workspaceDir: string): EnsureTargetsResult;
23
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../source/run/clone.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAElE;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;CACxC;AAGD;;;GAGG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,YAAY,EAAE,MAAM,GAClB,mBAAmB,CA+BrB"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Clone target repositories into the run workspace. The audited repos are named
3
+ * in sentinel.yaml, not known to the workflow statically, so `sentinel run`
4
+ * clones any that are not already present. Arg building is pure and tested; the
5
+ * gh spawn and fs checks are not.
6
+ */
7
+ import { spawnSync } from 'node:child_process';
8
+ import { existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ /** Build the `gh repo clone` argv for a shallow single-branch clone. */
11
+ export function buildCloneArgs(repo, dir) {
12
+ return ['repo', 'clone', repo, dir, '--', '--depth', '1', '--single-branch'];
13
+ }
14
+ /* c8 ignore start -- spawns gh and touches the filesystem. */
15
+ /**
16
+ * Ensure each repo is checked out under `workspaceDir/<owner>/<name>`, cloning
17
+ * any that are missing. Repos already present are left untouched.
18
+ */
19
+ export function ensureTargets(repos, workspaceDir) {
20
+ const result = { cloned: [], skipped: [], errors: [] };
21
+ for (const repo of repos) {
22
+ const dir = join(workspaceDir, repo);
23
+ if (existsSync(dir)) {
24
+ result.skipped.push(repo);
25
+ continue;
26
+ }
27
+ const run = spawnSync('gh', buildCloneArgs(repo, dir), {
28
+ encoding: 'utf8',
29
+ stdio: ['ignore', 'pipe', 'pipe'],
30
+ env: process.env,
31
+ });
32
+ if (run.error) {
33
+ const code = run.error.code;
34
+ result.errors.push({
35
+ repo,
36
+ error: code === 'ENOENT'
37
+ ? '`gh` is not on PATH. Install the GitHub CLI.'
38
+ : String(run.error),
39
+ });
40
+ }
41
+ else if (run.status !== 0) {
42
+ result.errors.push({ repo, error: (run.stderr ?? '').trim() });
43
+ }
44
+ else {
45
+ result.cloned.push(repo);
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ /* c8 ignore stop */
51
+ //# sourceMappingURL=clone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.js","sourceRoot":"","sources":["../../source/run/clone.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAC,UAAU,EAAC,MAAM,SAAS,CAAC;AACnC,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAE/B,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,GAAW;IACvD,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;AAC9E,CAAC;AASD,8DAA8D;AAC9D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAe,EACf,YAAoB;IAEpB,MAAM,MAAM,GAAwB,EAAC,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAC,CAAC;IAE1E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACrC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;YACtD,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,GAAG,EAAE,OAAO,CAAC,GAAG;SAChB,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,GAAI,GAAG,CAAC,KAA+B,CAAC,IAAI,CAAC;YACvD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBAClB,IAAI;gBACJ,KAAK,EACJ,IAAI,KAAK,QAAQ;oBAChB,CAAC,CAAC,8CAA8C;oBAChD,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;aACrB,CAAC,CAAC;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAC,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AACD,oBAAoB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Dry-run preview. Computes what a live run *would* do — without mutating
3
+ * anything — by running the same override, threshold, and dedup planning
4
+ * read-only, then grouping the findings into the buckets the whitepaper calls
5
+ * for: would file as new, dedup would have matched, below severity threshold
6
+ * (see docs/workflow/index.md#run-modes).
7
+ */
8
+ import type { RepoOverride, SentinelConfig } from '../config/types.js';
9
+ import { type ReconcileOptions } from '../dedup/plan.js';
10
+ import { type Finding } from '../findings/types.js';
11
+ import type { ExistingIssue } from '../issues/types.js';
12
+ /** The grouped outcome a live run would produce. */
13
+ export interface DryRunPreview {
14
+ /** Findings with no existing issue: a live run would file these. */
15
+ wouldFileAsNew: Finding[];
16
+ /** Findings matching an existing open issue: dedup would touch, not refile. */
17
+ dedupWouldMatch: Finding[];
18
+ /** Findings below the effective threshold: never filed. */
19
+ belowThreshold: Finding[];
20
+ /** Findings removed by the per-repo override's suppress rules. */
21
+ suppressedByOverride: Finding[];
22
+ /** Findings matching a dismissed (false-positive/wontfix/accepted) issue. */
23
+ suppressedByLabel: Finding[];
24
+ /** Open issues a live run would auto-resolve as stale. */
25
+ wouldResolve: number;
26
+ }
27
+ /** Compute the dry-run preview for one repo's findings. */
28
+ export declare function previewReconciliation(findings: Finding[], config: SentinelConfig, existing: ExistingIssue[], override?: RepoOverride, options?: ReconcileOptions): DryRunPreview;
29
+ /** Render the dry-run previews for every repo as Markdown. */
30
+ export declare function renderPreview(previews: {
31
+ repo: string;
32
+ preview: DryRunPreview;
33
+ }[]): string;
34
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../source/run/preview.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAC,KAAK,OAAO,EAAyB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AAGtD,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC7B,oEAAoE;IACpE,cAAc,EAAE,OAAO,EAAE,CAAC;IAC1B,+EAA+E;IAC/E,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,cAAc,EAAE,OAAO,EAAE,CAAC;IAC1B,kEAAkE;IAClE,oBAAoB,EAAE,OAAO,EAAE,CAAC;IAChC,6EAA6E;IAC7E,iBAAiB,EAAE,OAAO,EAAE,CAAC;IAC7B,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,OAAO,EAAE,EACnB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,aAAa,EAAE,EACzB,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,GAAE,gBAAqB,GAC5B,aAAa,CA2Bf;AAgBD,8DAA8D;AAC9D,wBAAgB,aAAa,CAC5B,QAAQ,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAC,EAAE,GAChD,MAAM,CAwBR"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Dry-run preview. Computes what a live run *would* do — without mutating
3
+ * anything — by running the same override, threshold, and dedup planning
4
+ * read-only, then grouping the findings into the buckets the whitepaper calls
5
+ * for: would file as new, dedup would have matched, below severity threshold
6
+ * (see docs/workflow/index.md#run-modes).
7
+ */
8
+ import { planReconciliation } from '../dedup/plan.js';
9
+ import { meetsSeverityThreshold } from '../findings/types.js';
10
+ import { applyRepoOverride } from '../suppression/apply.js';
11
+ /** Compute the dry-run preview for one repo's findings. */
12
+ export function previewReconciliation(findings, config, existing, override, options = {}) {
13
+ const { kept, suppressed: suppressedByOverride, threshold, } = applyRepoOverride(findings, config, override);
14
+ const belowThreshold = [];
15
+ const qualifying = [];
16
+ for (const finding of kept) {
17
+ if (meetsSeverityThreshold(finding.severity, threshold)) {
18
+ qualifying.push(finding);
19
+ }
20
+ else {
21
+ belowThreshold.push(finding);
22
+ }
23
+ }
24
+ const plan = planReconciliation(qualifying, existing, options);
25
+ return {
26
+ wouldFileAsNew: plan.toCreate,
27
+ dedupWouldMatch: plan.toTouch.map(op => op.finding),
28
+ belowThreshold,
29
+ suppressedByOverride,
30
+ suppressedByLabel: plan.suppressed,
31
+ wouldResolve: plan.toResolve.length,
32
+ };
33
+ }
34
+ function findingLine(finding) {
35
+ return `- **${finding.severity}** ${finding.summary ?? finding.rule} — \`${finding.file}\`:${finding.lineRange.start}`;
36
+ }
37
+ function group(title, findings) {
38
+ if (findings.length === 0) {
39
+ return `**${title}:** none`;
40
+ }
41
+ return [
42
+ `**${title}** (${findings.length}):`,
43
+ ...findings.map(findingLine),
44
+ ].join('\n');
45
+ }
46
+ /** Render the dry-run previews for every repo as Markdown. */
47
+ export function renderPreview(previews) {
48
+ const parts = ['# Sentinel dry run', '', 'No issues were filed.'];
49
+ for (const { repo, preview } of previews) {
50
+ parts.push([
51
+ `## ${repo}`,
52
+ '',
53
+ group('Would file as new', preview.wouldFileAsNew),
54
+ '',
55
+ group('Dedup would have matched', preview.dedupWouldMatch),
56
+ '',
57
+ group('Below severity threshold', preview.belowThreshold),
58
+ '',
59
+ group('Suppressed by per-repo override', preview.suppressedByOverride),
60
+ '',
61
+ group('Suppressed by a prior dismissal', preview.suppressedByLabel),
62
+ '',
63
+ `**Would auto-resolve:** ${preview.wouldResolve} stale issue(s)`,
64
+ ].join('\n'));
65
+ }
66
+ return parts.join('\n\n');
67
+ }
68
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../source/run/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Render a run's outcome as Markdown — the local `--output` file and the
3
+ * Actions step summary. Pure and tested.
4
+ */
5
+ import type { RunOutcome } from './types.js';
6
+ /** Count the findings across a run. */
7
+ export declare function countFindings(run: RunOutcome): number;
8
+ /** Render a full run as a Markdown report. */
9
+ export declare function renderReport(run: RunOutcome): string;
10
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../source/run/report.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAA2B,UAAU,EAAC,MAAM,YAAY,CAAC;AA2DrE,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAMrD;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAapD"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Render a run's outcome as Markdown — the local `--output` file and the
3
+ * Actions step summary. Pure and tested.
4
+ */
5
+ import { findingHash } from '../dedup/hash.js';
6
+ function findingSection(finding) {
7
+ const lines = [
8
+ `#### ${finding.severity.toUpperCase()} — ${finding.summary ?? finding.rule}`,
9
+ '',
10
+ `- **File:** \`${finding.file}\` lines ${finding.lineRange.start}–${finding.lineRange.end}`,
11
+ `- **Rule:** \`${finding.rule}\` · **Confidence:** ${finding.confidence} · **Category:** ${finding.category}`,
12
+ `- **Hash:** \`${findingHash(finding)}\``,
13
+ ];
14
+ if (finding.rationale) {
15
+ lines.push(`- **Why:** ${finding.rationale}`);
16
+ }
17
+ if (finding.suggestedNextSteps) {
18
+ lines.push(`- **Next:** ${finding.suggestedNextSteps}`);
19
+ }
20
+ return lines.join('\n');
21
+ }
22
+ function packSection(outcome) {
23
+ const header = `### Pack \`${outcome.pack}\` (v${outcome.version})`;
24
+ if (outcome.runError) {
25
+ return `${header}\n\n> Run error: ${outcome.runError}${rawBlock(outcome.raw)}`;
26
+ }
27
+ if (!outcome.ok) {
28
+ const detail = outcome.errors
29
+ .map(error => `${error.field}: ${error.message}`)
30
+ .join('; ');
31
+ return `${header}\n\n> Findings were malformed after ${outcome.attempts} attempt(s): ${detail}${rawBlock(outcome.raw)}`;
32
+ }
33
+ if (outcome.findings.length === 0) {
34
+ return `${header}\n\nNo findings.`;
35
+ }
36
+ return [header, '', ...outcome.findings.map(findingSection)].join('\n\n');
37
+ }
38
+ /** A collapsible block with the raw model output, for diagnosing a failure. */
39
+ function rawBlock(raw) {
40
+ if (!raw) {
41
+ return '';
42
+ }
43
+ const capped = raw.length > 6000 ? `${raw.slice(0, 6000)}\n…(truncated)` : raw;
44
+ return `\n\n<details><summary>Raw model output</summary>\n\n\`\`\`\n${capped}\n\`\`\`\n</details>`;
45
+ }
46
+ function repoSection(outcome) {
47
+ const parts = [`## ${outcome.repo}`];
48
+ if (outcome.missingPacks.length > 0) {
49
+ parts.push(`> Missing packs (not in rule-packs/): ${outcome.missingPacks.join(', ')}`);
50
+ }
51
+ for (const pack of outcome.packs) {
52
+ parts.push(packSection(pack));
53
+ }
54
+ return parts.join('\n\n');
55
+ }
56
+ /** Count the findings across a run. */
57
+ export function countFindings(run) {
58
+ return run.repos.reduce((total, repo) => total + repo.packs.reduce((sum, pack) => sum + pack.findings.length, 0), 0);
59
+ }
60
+ /** Render a full run as a Markdown report. */
61
+ export function renderReport(run) {
62
+ const total = countFindings(run);
63
+ const header = [
64
+ '# Sentinel audit report',
65
+ '',
66
+ `${total} finding(s) across ${run.repos.length} repository(ies).`,
67
+ ].join('\n');
68
+ if (run.repos.length === 0) {
69
+ return `${header}\n\nNo repositories were audited.`;
70
+ }
71
+ return [header, ...run.repos.map(repoSection)].join('\n\n');
72
+ }
73
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../source/run/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The `sentinel run` orchestrator. Drives the whole engine over a config:
3
+ * load packs, resolve each target's packs (+depends_on), gather the repo's
4
+ * files, audit each pack, then either reconcile findings into issues (Actions
5
+ * path) or leave the outcome for a Markdown report (local / dry-run path).
6
+ *
7
+ * All I/O is injected (RepoFiles, PackLoader, ModelRunner, ReconcileClient) so
8
+ * the orchestration is testable end-to-end with fakes.
9
+ */
10
+ import type { SentinelConfig } from '../config/types.js';
11
+ import { type ReconcileResult } from '../dedup/reconcile.js';
12
+ import type { ReconcileClient } from '../issues/types.js';
13
+ import type { AutoFixOptions } from '../orchestrator/auto-fix.js';
14
+ import type { ModelRunner } from '../orchestrator/types.js';
15
+ import { type DryRunPreview } from './preview.js';
16
+ import type { PackLoadError, PackLoader, RepoFiles, RunOutcome } from './types.js';
17
+ /** Injected dependencies for a run. */
18
+ export interface RunDeps {
19
+ runner: ModelRunner;
20
+ files: RepoFiles;
21
+ packs: PackLoader;
22
+ /** Present only when filing issues (the Actions path). */
23
+ client?: ReconcileClient;
24
+ /** ISO timestamp for deterministic reconciliation. */
25
+ now: string;
26
+ }
27
+ /** Options for a config-driven run. */
28
+ export interface RunConfigOptions {
29
+ /** Directory the target repos are checked out under. */
30
+ workspaceDir: string;
31
+ /** The config repo's rule-packs directory. */
32
+ packsDir: string;
33
+ /** The config repo's owner/name, for issue routing and footers. */
34
+ configRepo?: string;
35
+ /**
36
+ * The config repo directory holding nanocoder's agents.config.json. Passed
37
+ * to the runner as NANOCODER_CONFIG_DIR so provider wiring lives there.
38
+ */
39
+ configDir?: string;
40
+ /** Audit but file nothing. */
41
+ dryRun?: boolean;
42
+ autoFix?: AutoFixOptions;
43
+ resolveAfterMisses?: number;
44
+ }
45
+ /** Everything a config-driven run produced. */
46
+ export interface RunReport {
47
+ outcome: RunOutcome;
48
+ /** Live-run reconciliation results (empty on a dry run). */
49
+ reconciled: {
50
+ repo: string;
51
+ result: ReconcileResult;
52
+ }[];
53
+ /** Dry-run previews (empty on a live run). */
54
+ previews: {
55
+ repo: string;
56
+ preview: DryRunPreview;
57
+ }[];
58
+ packLoadErrors: PackLoadError[];
59
+ /** True if issues were filed (client present and not a dry run). */
60
+ filed: boolean;
61
+ }
62
+ /** Run an audit driven by a Sentinel config. */
63
+ export declare function runFromConfig(config: SentinelConfig, deps: RunDeps, options: RunConfigOptions): Promise<RunReport>;
64
+ /** Dependencies for an ad-hoc local run of a single pack. */
65
+ export interface RunLocalDeps {
66
+ runner: ModelRunner;
67
+ files: RepoFiles;
68
+ }
69
+ /**
70
+ * Run a single pack against a repository directory for off-cycle calibration.
71
+ * Never files issues. Throws if the pack file is missing or invalid.
72
+ */
73
+ export declare function runLocal(packPath: string, repoDir: string, model: SentinelConfig['model'], deps: RunLocalDeps, options?: AutoFixOptions): Promise<RunOutcome>;
74
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../source/run/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAe,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAC,KAAK,eAAe,EAAoB,MAAM,uBAAuB,CAAC;AAE9E,OAAO,KAAK,EAAgB,eAAe,EAAC,MAAM,oBAAoB,CAAC;AACvE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAC,KAAK,aAAa,EAAwB,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EACX,aAAa,EACb,UAAU,EAEV,SAAS,EAET,UAAU,EACV,MAAM,YAAY,CAAC;AAEpB,uCAAuC;AACvC,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAChC,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,+CAA+C;AAC/C,MAAM,WAAW,SAAS;IACzB,OAAO,EAAE,UAAU,CAAC;IACpB,4DAA4D;IAC5D,UAAU,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,eAAe,CAAA;KAAC,EAAE,CAAC;IACtD,8CAA8C;IAC9C,QAAQ,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAC,EAAE,CAAC;IACnD,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,oEAAoE;IACpE,KAAK,EAAE,OAAO,CAAC;CACf;AAcD,gDAAgD;AAChD,wBAAsB,aAAa,CAClC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,SAAS,CAAC,CAkHpB;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAC9B,IAAI,EAAE,YAAY,EAClB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAwBrB"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The `sentinel run` orchestrator. Drives the whole engine over a config:
3
+ * load packs, resolve each target's packs (+depends_on), gather the repo's
4
+ * files, audit each pack, then either reconcile findings into issues (Actions
5
+ * path) or leave the outcome for a Markdown report (local / dry-run path).
6
+ *
7
+ * All I/O is injected (RepoFiles, PackLoader, ModelRunner, ReconcileClient) so
8
+ * the orchestration is testable end-to-end with fakes.
9
+ */
10
+ import { join } from 'node:path';
11
+ import { parseRepoOverride } from '../config/repo-override.js';
12
+ import { reconcileFindings } from '../dedup/reconcile.js';
13
+ import { targetRepoFor } from '../issues/file.js';
14
+ import { resolveDependencies } from '../rule-packs/dependencies.js';
15
+ import { parseRulePack } from '../rule-packs/parse.js';
16
+ import { auditPack } from './audit.js';
17
+ import { previewReconciliation } from './preview.js';
18
+ import { unionPatterns } from './select.js';
19
+ async function readOverride(files, repoDir) {
20
+ const text = await files.readText(join(repoDir, 'sentinel.yaml'));
21
+ if (text === null) {
22
+ return undefined;
23
+ }
24
+ const parsed = parseRepoOverride(text);
25
+ return parsed.valid && parsed.override ? parsed.override : undefined;
26
+ }
27
+ /** Run an audit driven by a Sentinel config. */
28
+ export async function runFromConfig(config, deps, options) {
29
+ const loaded = await deps.packs.load(options.packsDir);
30
+ const packByName = new Map(loaded.packs.map(pack => [pack.manifest.name, pack]));
31
+ const repos = [];
32
+ const reconciled = [];
33
+ const previews = [];
34
+ const filing = Boolean(deps.client) && !options.dryRun;
35
+ for (const target of config.targets) {
36
+ // Pattern targets need org repo enumeration (a GitHub API concern); v1
37
+ // audits explicit repos.
38
+ if (!target.repo) {
39
+ continue;
40
+ }
41
+ const repoName = target.repo;
42
+ const repoDir = join(options.workspaceDir, repoName);
43
+ const resolvedNames = new Set();
44
+ const missingPacks = [];
45
+ for (const name of target.rulePacks) {
46
+ if (!packByName.has(name)) {
47
+ missingPacks.push(name);
48
+ continue;
49
+ }
50
+ const resolved = resolveDependencies(loaded.packs, name);
51
+ if (resolved.errors.length > 0) {
52
+ missingPacks.push(name);
53
+ continue;
54
+ }
55
+ for (const resolvedName of resolved.order) {
56
+ resolvedNames.add(resolvedName);
57
+ }
58
+ }
59
+ const resolvedPacks = [];
60
+ for (const name of resolvedNames) {
61
+ const pack = packByName.get(name);
62
+ if (pack) {
63
+ resolvedPacks.push(pack);
64
+ }
65
+ }
66
+ const files = await deps.files.read(repoDir, unionPatterns(resolvedPacks));
67
+ const runnerOptions = {
68
+ ...options.autoFix,
69
+ cwd: repoDir,
70
+ configDir: options.configDir,
71
+ };
72
+ const packOutcomes = [];
73
+ for (const pack of resolvedPacks) {
74
+ packOutcomes.push(await auditPack(pack, { repoName, files }, config.model, deps.runner, runnerOptions));
75
+ }
76
+ repos.push({ repo: repoName, packs: packOutcomes, missingPacks });
77
+ if (!deps.client) {
78
+ continue;
79
+ }
80
+ const findings = packOutcomes.flatMap(outcome => outcome.findings);
81
+ const override = await readOverride(deps.files, repoDir);
82
+ const context = {
83
+ auditedRepo: repoName,
84
+ configRepo: options.configRepo,
85
+ };
86
+ if (filing) {
87
+ const result = await reconcileFindings(findings, config, deps.client, context, deps.now, { resolveAfterMisses: options.resolveAfterMisses }, override);
88
+ reconciled.push({ repo: repoName, result });
89
+ }
90
+ else {
91
+ // Dry run: read existing issues and compute the preview, mutating nothing.
92
+ const existing = await deps.client.listIssues({
93
+ repo: targetRepoFor(config, context),
94
+ label: config.issues.label,
95
+ });
96
+ const preview = previewReconciliation(findings, config, existing, override, {
97
+ resolveAfterMisses: options.resolveAfterMisses,
98
+ });
99
+ previews.push({ repo: repoName, preview });
100
+ }
101
+ }
102
+ return {
103
+ outcome: { repos },
104
+ reconciled,
105
+ previews,
106
+ packLoadErrors: loaded.errors,
107
+ filed: filing,
108
+ };
109
+ }
110
+ /**
111
+ * Run a single pack against a repository directory for off-cycle calibration.
112
+ * Never files issues. Throws if the pack file is missing or invalid.
113
+ */
114
+ export async function runLocal(packPath, repoDir, model, deps, options = {}) {
115
+ const text = await deps.files.readText(packPath);
116
+ if (text === null) {
117
+ throw new Error(`rule pack not found: ${packPath}`);
118
+ }
119
+ const parsed = parseRulePack(text);
120
+ if (!parsed.valid || !parsed.pack) {
121
+ const detail = parsed.errors
122
+ .map(error => `${error.field}: ${error.message}`)
123
+ .join('; ');
124
+ throw new Error(`invalid rule pack ${packPath}: ${detail}`);
125
+ }
126
+ const pack = parsed.pack;
127
+ const files = await deps.files.read(repoDir, pack.manifest.appliesTo.paths);
128
+ const outcome = await auditPack(pack, { repoName: repoDir, files }, model, deps.runner, { ...options, cwd: repoDir });
129
+ return { repos: [{ repo: repoDir, packs: [outcome], missingPacks: [] }] };
130
+ }
131
+ //# sourceMappingURL=run.js.map