@kogu/context-check 0.1.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.
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/analyzer/engine.d.ts +27 -0
- package/dist/analyzer/engine.js +64 -0
- package/dist/analyzer/engine.js.map +1 -0
- package/dist/analyzer/rules/context-overhead.d.ts +21 -0
- package/dist/analyzer/rules/context-overhead.js +53 -0
- package/dist/analyzer/rules/context-overhead.js.map +1 -0
- package/dist/analyzer/rules/duplicate.d.ts +15 -0
- package/dist/analyzer/rules/duplicate.js +53 -0
- package/dist/analyzer/rules/duplicate.js.map +1 -0
- package/dist/analyzer/rules/high-stakes.d.ts +36 -0
- package/dist/analyzer/rules/high-stakes.js +96 -0
- package/dist/analyzer/rules/high-stakes.js.map +1 -0
- package/dist/analyzer/rules/large-file.d.ts +15 -0
- package/dist/analyzer/rules/large-file.js +41 -0
- package/dist/analyzer/rules/large-file.js.map +1 -0
- package/dist/analyzer/rules/mapping.d.ts +29 -0
- package/dist/analyzer/rules/mapping.js +51 -0
- package/dist/analyzer/rules/mapping.js.map +1 -0
- package/dist/analyzer/rules/repetition.d.ts +20 -0
- package/dist/analyzer/rules/repetition.js +80 -0
- package/dist/analyzer/rules/repetition.js.map +1 -0
- package/dist/analyzer/rules/scoped-no-match.d.ts +15 -0
- package/dist/analyzer/rules/scoped-no-match.js +46 -0
- package/dist/analyzer/rules/scoped-no-match.js.map +1 -0
- package/dist/analyzer/run.d.ts +25 -0
- package/dist/analyzer/run.js +59 -0
- package/dist/analyzer/run.js.map +1 -0
- package/dist/analyzer/severity.d.ts +21 -0
- package/dist/analyzer/severity.js +39 -0
- package/dist/analyzer/severity.js.map +1 -0
- package/dist/analyzer/types.d.ts +35 -0
- package/dist/analyzer/types.js +17 -0
- package/dist/analyzer/types.js.map +1 -0
- package/dist/cli/index.d.ts +23 -0
- package/dist/cli/index.js +159 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/load.d.ts +11 -0
- package/dist/config/load.js +38 -0
- package/dist/config/load.js.map +1 -0
- package/dist/discovery/adapters/agents.d.ts +8 -0
- package/dist/discovery/adapters/agents.js +25 -0
- package/dist/discovery/adapters/agents.js.map +1 -0
- package/dist/discovery/adapters/artifact.d.ts +21 -0
- package/dist/discovery/adapters/artifact.js +40 -0
- package/dist/discovery/adapters/artifact.js.map +1 -0
- package/dist/discovery/adapters/claude.d.ts +8 -0
- package/dist/discovery/adapters/claude.js +25 -0
- package/dist/discovery/adapters/claude.js.map +1 -0
- package/dist/discovery/adapters/cursor.d.ts +11 -0
- package/dist/discovery/adapters/cursor.js +32 -0
- package/dist/discovery/adapters/cursor.js.map +1 -0
- package/dist/discovery/adapters/index.d.ts +10 -0
- package/dist/discovery/adapters/index.js +18 -0
- package/dist/discovery/adapters/index.js.map +1 -0
- package/dist/discovery/adapters/skill.d.ts +9 -0
- package/dist/discovery/adapters/skill.js +26 -0
- package/dist/discovery/adapters/skill.js.map +1 -0
- package/dist/discovery/adapters/types.d.ts +22 -0
- package/dist/discovery/adapters/types.js +9 -0
- package/dist/discovery/adapters/types.js.map +1 -0
- package/dist/discovery/discover.d.ts +32 -0
- package/dist/discovery/discover.js +51 -0
- package/dist/discovery/discover.js.map +1 -0
- package/dist/discovery/exclusions.d.ts +19 -0
- package/dist/discovery/exclusions.js +40 -0
- package/dist/discovery/exclusions.js.map +1 -0
- package/dist/discovery/scanner.d.ts +31 -0
- package/dist/discovery/scanner.js +75 -0
- package/dist/discovery/scanner.js.map +1 -0
- package/dist/git/history.d.ts +25 -0
- package/dist/git/history.js +71 -0
- package/dist/git/history.js.map +1 -0
- package/dist/git/sufficiency.d.ts +38 -0
- package/dist/git/sufficiency.js +98 -0
- package/dist/git/sufficiency.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/output/json.d.ts +58 -0
- package/dist/output/json.js +52 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/terminal.d.ts +21 -0
- package/dist/output/terminal.js +163 -0
- package/dist/output/terminal.js.map +1 -0
- package/dist/parser/frontmatter.d.ts +26 -0
- package/dist/parser/frontmatter.js +63 -0
- package/dist/parser/frontmatter.js.map +1 -0
- package/dist/parser/markdown.d.ts +23 -0
- package/dist/parser/markdown.js +63 -0
- package/dist/parser/markdown.js.map +1 -0
- package/dist/parser/normalize.d.ts +20 -0
- package/dist/parser/normalize.js +35 -0
- package/dist/parser/normalize.js.map +1 -0
- package/dist/parser/parse.d.ts +34 -0
- package/dist/parser/parse.js +35 -0
- package/dist/parser/parse.js.map +1 -0
- package/dist/parser/plain-text.d.ts +30 -0
- package/dist/parser/plain-text.js +52 -0
- package/dist/parser/plain-text.js.map +1 -0
- package/dist/parser/scope.d.ts +20 -0
- package/dist/parser/scope.js +44 -0
- package/dist/parser/scope.js.map +1 -0
- package/dist/snapshot/diff.d.ts +62 -0
- package/dist/snapshot/diff.js +146 -0
- package/dist/snapshot/diff.js.map +1 -0
- package/dist/snapshot/manager.d.ts +54 -0
- package/dist/snapshot/manager.js +131 -0
- package/dist/snapshot/manager.js.map +1 -0
- package/dist/tokens/estimator.d.ts +12 -0
- package/dist/tokens/estimator.js +16 -0
- package/dist/tokens/estimator.js.map +1 -0
- package/dist/types/configuration.d.ts +47 -0
- package/dist/types/configuration.js +9 -0
- package/dist/types/configuration.js.map +1 -0
- package/dist/types/finding.d.ts +32 -0
- package/dist/types/finding.js +14 -0
- package/dist/types/finding.js.map +1 -0
- package/dist/types/report.d.ts +20 -0
- package/dist/types/report.js +9 -0
- package/dist/types/report.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large instruction detection (Spec §20).
|
|
3
|
+
*
|
|
4
|
+
* A file being large is not inherently bad, but a file above the token
|
|
5
|
+
* threshold may contribute significantly to model context. In the MVP both
|
|
6
|
+
* the notice and warning thresholds map to `label=CONTEXT`, `severity=info`
|
|
7
|
+
* through the normative mapping; we only ever emit `large-file` findings.
|
|
8
|
+
*
|
|
9
|
+
* Note: the `warningFileTokens` threshold is reserved (Spec §11) and produces
|
|
10
|
+
* no additional severity in the MVP — the taxonomy keeps `info` here.
|
|
11
|
+
*/
|
|
12
|
+
import { DEFAULT_ANALYSIS_OPTIONS } from "../types.js";
|
|
13
|
+
import { makeFinding } from "./mapping.js";
|
|
14
|
+
export const largeFileRule = {
|
|
15
|
+
name: "large-file",
|
|
16
|
+
run(artifacts, context) {
|
|
17
|
+
const threshold = context.options.largeFileTokens ??
|
|
18
|
+
DEFAULT_ANALYSIS_OPTIONS.largeFileTokens;
|
|
19
|
+
const findings = [];
|
|
20
|
+
for (const artifact of artifacts) {
|
|
21
|
+
const tokens = artifact.metadata.estimatedTokens;
|
|
22
|
+
if (tokens <= threshold)
|
|
23
|
+
continue;
|
|
24
|
+
findings.push(makeFinding({
|
|
25
|
+
type: "large-file",
|
|
26
|
+
filePaths: [artifact.path],
|
|
27
|
+
title: "Instruction file is unusually large",
|
|
28
|
+
description: `${artifact.path} is estimated at ~${formatTokens(tokens)} tokens. ` +
|
|
29
|
+
`This file is relatively large and may contribute significantly to model context.`,
|
|
30
|
+
recommendation: `Consider whether all sections are still needed, or split unrelated ` +
|
|
31
|
+
`instructions into smaller scoped files.`,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
return findings;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/** Formats a token count with thousands separators, e.g. 4800 -> "4,800". */
|
|
38
|
+
export function formatTokens(tokens) {
|
|
39
|
+
return tokens.toLocaleString("en-US");
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=large-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"large-file.js","sourceRoot":"","sources":["../../../src/analyzer/rules/large-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,wBAAwB,EAAqB,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,IAAI,EAAE,YAAY;IAClB,GAAG,CAAC,SAAS,EAAE,OAAO;QACpB,MAAM,SAAS,GACb,OAAO,CAAC,OAAO,CAAC,eAAe;YAC/B,wBAAwB,CAAC,eAAe,CAAC;QAC3C,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC;YACjD,IAAI,MAAM,IAAI,SAAS;gBAAE,SAAS;YAElC,QAAQ,CAAC,IAAI,CACX,WAAW,CAAC;gBACV,IAAI,EAAE,YAAY;gBAClB,SAAS,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,qCAAqC;gBAC5C,WAAW,EACT,GAAG,QAAQ,CAAC,IAAI,qBAAqB,YAAY,CAAC,MAAM,CAAC,WAAW;oBACpE,kFAAkF;gBACpF,cAAc,EACZ,qEAAqE;oBACrE,yCAAyC;aAC5C,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding factory + normative type/label/severity/confidence mapping
|
|
3
|
+
* (Spec §10, §13, Rule 17, Rule 27).
|
|
4
|
+
*
|
|
5
|
+
* Analyzers must never invent their own `label`, `severity` or `confidence`
|
|
6
|
+
* values. This mapping is the single source of truth; a rule only supplies a
|
|
7
|
+
* `type` and the factory resolves the public fields.
|
|
8
|
+
*/
|
|
9
|
+
import type { Finding, FindingConfidence, FindingLabel, FindingSeverity, FindingType } from "../../types/finding.js";
|
|
10
|
+
export interface FindingSpec {
|
|
11
|
+
type: FindingType;
|
|
12
|
+
filePaths: string[];
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
recommendation?: string;
|
|
16
|
+
}
|
|
17
|
+
interface NormativeMeta {
|
|
18
|
+
label: FindingLabel;
|
|
19
|
+
severity: FindingSeverity;
|
|
20
|
+
confidence: FindingConfidence;
|
|
21
|
+
}
|
|
22
|
+
/** Normative mapping (Spec §10 table). */
|
|
23
|
+
export declare const NORMATIVE_MAPPING: Record<FindingType, NormativeMeta>;
|
|
24
|
+
/**
|
|
25
|
+
* Produces a fully-populated Finding with a deterministic id and the
|
|
26
|
+
* normative label/severity/confidence resolved from `type`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function makeFinding(spec: FindingSpec): Finding;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding factory + normative type/label/severity/confidence mapping
|
|
3
|
+
* (Spec §10, §13, Rule 17, Rule 27).
|
|
4
|
+
*
|
|
5
|
+
* Analyzers must never invent their own `label`, `severity` or `confidence`
|
|
6
|
+
* values. This mapping is the single source of truth; a rule only supplies a
|
|
7
|
+
* `type` and the factory resolves the public fields.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
/** Normative mapping (Spec §10 table). */
|
|
11
|
+
export const NORMATIVE_MAPPING = {
|
|
12
|
+
duplicate: { label: "INFO", severity: "info", confidence: "high" },
|
|
13
|
+
repetition: { label: "INFO", severity: "info", confidence: "medium" },
|
|
14
|
+
"large-file": { label: "CONTEXT", severity: "info", confidence: "high" },
|
|
15
|
+
"scoped-no-match": {
|
|
16
|
+
label: "REVIEW",
|
|
17
|
+
severity: "notice",
|
|
18
|
+
confidence: "medium",
|
|
19
|
+
},
|
|
20
|
+
"high-stakes": { label: "CAUTION", severity: "notice", confidence: "medium" },
|
|
21
|
+
"context-overhead": {
|
|
22
|
+
label: "CONTEXT",
|
|
23
|
+
severity: "info",
|
|
24
|
+
confidence: "medium",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Produces a fully-populated Finding with a deterministic id and the
|
|
29
|
+
* normative label/severity/confidence resolved from `type`.
|
|
30
|
+
*/
|
|
31
|
+
export function makeFinding(spec) {
|
|
32
|
+
const meta = NORMATIVE_MAPPING[spec.type];
|
|
33
|
+
const id = findingId(spec.type, spec.filePaths, spec.title);
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
type: spec.type,
|
|
37
|
+
label: meta.label,
|
|
38
|
+
severity: meta.severity,
|
|
39
|
+
confidence: meta.confidence,
|
|
40
|
+
filePaths: [...spec.filePaths],
|
|
41
|
+
title: spec.title,
|
|
42
|
+
description: spec.description,
|
|
43
|
+
recommendation: spec.recommendation,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Deterministic id from type + paths + title. */
|
|
47
|
+
function findingId(type, filePaths, title) {
|
|
48
|
+
const key = `${type}::${[...filePaths].sort().join(",")}::${title}`;
|
|
49
|
+
return `finding_${createHash("sha1").update(key).digest("hex").slice(0, 8)}`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../../src/analyzer/rules/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAwBzC,0CAA0C;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAuC;IACnE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;IAClE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE;IACrE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;IACxE,iBAAiB,EAAE;QACjB,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,QAAQ;QAClB,UAAU,EAAE,QAAQ;KACrB;IACD,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE;IAC7E,kBAAkB,EAAE;QAClB,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,MAAM;QAChB,UAAU,EAAE,QAAQ;KACrB;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAiB;IAC3C,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO;QACL,EAAE;QACF,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;AACJ,CAAC;AAED,kDAAkD;AAClD,SAAS,SAAS,CAChB,IAAiB,EACjB,SAAmB,EACnB,KAAa;IAEb,MAAM,GAAG,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;IACpE,OAAO,WAAW,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repetition detection (Spec §18, Rule 21).
|
|
3
|
+
*
|
|
4
|
+
* Two strategies:
|
|
5
|
+
* - Markdown files: compare `heading + normalized section content`.
|
|
6
|
+
* - Files without headings: plain-text block fallback (paragraph/block
|
|
7
|
+
* chunking), ignoring very short blocks.
|
|
8
|
+
*
|
|
9
|
+
* Only exact normalized text blocks are repetition candidates. The MVP does
|
|
10
|
+
* NOT use semantic similarity (Rule 13): semantically similar but textually
|
|
11
|
+
* different instructions are not flagged.
|
|
12
|
+
*/
|
|
13
|
+
import type { ConfigurationArtifact } from "../../types/configuration.js";
|
|
14
|
+
import type { AnalyzerRule } from "../types.js";
|
|
15
|
+
export declare const repetitionRule: AnalyzerRule;
|
|
16
|
+
/** Extracts comparison units (markdown sections or plain-text blocks). */
|
|
17
|
+
export declare function buildUnits(artifact: ConfigurationArtifact): Array<{
|
|
18
|
+
key: string;
|
|
19
|
+
display: string;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repetition detection (Spec §18, Rule 21).
|
|
3
|
+
*
|
|
4
|
+
* Two strategies:
|
|
5
|
+
* - Markdown files: compare `heading + normalized section content`.
|
|
6
|
+
* - Files without headings: plain-text block fallback (paragraph/block
|
|
7
|
+
* chunking), ignoring very short blocks.
|
|
8
|
+
*
|
|
9
|
+
* Only exact normalized text blocks are repetition candidates. The MVP does
|
|
10
|
+
* NOT use semantic similarity (Rule 13): semantically similar but textually
|
|
11
|
+
* different instructions are not flagged.
|
|
12
|
+
*/
|
|
13
|
+
import { extractMarkdownSections, } from "../../parser/markdown.js";
|
|
14
|
+
import { normalizeForComparison } from "../../parser/normalize.js";
|
|
15
|
+
import { chunkPlainText } from "../../parser/plain-text.js";
|
|
16
|
+
import { stripFrontmatter } from "../../parser/normalize.js";
|
|
17
|
+
import { makeFinding } from "./mapping.js";
|
|
18
|
+
export const repetitionRule = {
|
|
19
|
+
name: "repetition",
|
|
20
|
+
run(artifacts) {
|
|
21
|
+
const units = [];
|
|
22
|
+
for (const artifact of artifacts) {
|
|
23
|
+
for (const unit of buildUnits(artifact)) {
|
|
24
|
+
units.push({
|
|
25
|
+
artifactPath: artifact.path,
|
|
26
|
+
key: unit.key,
|
|
27
|
+
display: unit.display,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Group by normalized key; a key appearing more than once is a repetition.
|
|
32
|
+
const byKey = new Map();
|
|
33
|
+
for (const unit of units) {
|
|
34
|
+
const list = byKey.get(unit.key) ?? [];
|
|
35
|
+
list.push(unit);
|
|
36
|
+
byKey.set(unit.key, list);
|
|
37
|
+
}
|
|
38
|
+
const findings = [];
|
|
39
|
+
for (const matches of byKey.values()) {
|
|
40
|
+
if (matches.length < 2)
|
|
41
|
+
continue;
|
|
42
|
+
const paths = [...new Set(matches.map((m) => m.artifactPath))];
|
|
43
|
+
const detail = matches
|
|
44
|
+
.map((m) => `${m.artifactPath} (${m.display})`)
|
|
45
|
+
.slice(0, 3)
|
|
46
|
+
.join("; ");
|
|
47
|
+
findings.push(makeFinding({
|
|
48
|
+
type: "repetition",
|
|
49
|
+
filePaths: paths,
|
|
50
|
+
title: "Repeated instruction section",
|
|
51
|
+
description: `A normalized section appears ${matches.length} times across ` +
|
|
52
|
+
`${paths.length} file(s): ${detail}.`,
|
|
53
|
+
recommendation: `Review whether the repeated guidance can be consolidated into a ` +
|
|
54
|
+
`single source. Textually identical, not semantically similar.`,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
return findings;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/** Extracts comparison units (markdown sections or plain-text blocks). */
|
|
61
|
+
export function buildUnits(artifact) {
|
|
62
|
+
const body = stripFrontmatter(artifact.content);
|
|
63
|
+
const sections = extractMarkdownSections(body);
|
|
64
|
+
if (sections.length > 0) {
|
|
65
|
+
return sections.map((section) => sectionUnit(section));
|
|
66
|
+
}
|
|
67
|
+
return chunkPlainText(body).map((block) => blockUnit(block));
|
|
68
|
+
}
|
|
69
|
+
function sectionUnit(section) {
|
|
70
|
+
// heading + normalized content (Spec §18).
|
|
71
|
+
const key = normalizeForComparison(`${section.heading}${section.content}`);
|
|
72
|
+
return { key, display: `# ${section.heading}` };
|
|
73
|
+
}
|
|
74
|
+
function blockUnit(block) {
|
|
75
|
+
const preview = block.normalized.length > 24
|
|
76
|
+
? `${block.normalized.slice(0, 24)}...`
|
|
77
|
+
: block.normalized;
|
|
78
|
+
return { key: block.normalized, display: `"${preview}"` };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=repetition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repetition.js","sourceRoot":"","sources":["../../../src/analyzer/rules/repetition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoped no-match finding (Spec §23, §25, Rule 18).
|
|
3
|
+
*
|
|
4
|
+
* Only runs when Git history is sufficient. For each artifact with `globs`
|
|
5
|
+
* (scope patterns), if NONE of its patterns matched any file change in the
|
|
6
|
+
* window, produce a `scoped-no-match` review finding.
|
|
7
|
+
*
|
|
8
|
+
* This is a REVIEW CANDIDATE, never an "unused"/"obsolete" label (Rule 8,
|
|
9
|
+
* Rule 9). Git activity alone is not evidence a rule is dead.
|
|
10
|
+
*/
|
|
11
|
+
import type { ConfigurationArtifact } from "../../types/configuration.js";
|
|
12
|
+
import type { AnalyzerRule } from "../types.js";
|
|
13
|
+
export declare const scopedNoMatchRule: AnalyzerRule;
|
|
14
|
+
/** Whether an artifact has glob-scoped patterns that scoped analysis applies to. */
|
|
15
|
+
export declare function hasScopedPatterns(artifact: ConfigurationArtifact): boolean;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoped no-match finding (Spec §23, §25, Rule 18).
|
|
3
|
+
*
|
|
4
|
+
* Only runs when Git history is sufficient. For each artifact with `globs`
|
|
5
|
+
* (scope patterns), if NONE of its patterns matched any file change in the
|
|
6
|
+
* window, produce a `scoped-no-match` review finding.
|
|
7
|
+
*
|
|
8
|
+
* This is a REVIEW CANDIDATE, never an "unused"/"obsolete" label (Rule 8,
|
|
9
|
+
* Rule 9). Git activity alone is not evidence a rule is dead.
|
|
10
|
+
*/
|
|
11
|
+
import { matchesChangedFile } from "../../git/history.js";
|
|
12
|
+
import { makeFinding } from "./mapping.js";
|
|
13
|
+
export const scopedNoMatchRule = {
|
|
14
|
+
name: "scoped-no-match",
|
|
15
|
+
run(artifacts, context) {
|
|
16
|
+
// Rule 18: skip entirely when Git history is insufficient.
|
|
17
|
+
if (!context.gitHistorySufficient) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const changed = context.changedFilePaths ?? [];
|
|
21
|
+
const findings = [];
|
|
22
|
+
for (const artifact of artifacts) {
|
|
23
|
+
const patterns = artifact.scope?.patterns;
|
|
24
|
+
if (!patterns || patterns.length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
const anyMatched = patterns.some((pattern) => matchesChangedFile(pattern, changed));
|
|
27
|
+
if (anyMatched)
|
|
28
|
+
continue;
|
|
29
|
+
findings.push(makeFinding({
|
|
30
|
+
type: "scoped-no-match",
|
|
31
|
+
filePaths: [artifact.path],
|
|
32
|
+
title: "No matching file changes for scoped rule",
|
|
33
|
+
description: `No file changes matched the scoped patterns for ${artifact.path} ` +
|
|
34
|
+
`in the analyzed Git history window.`,
|
|
35
|
+
recommendation: `This does NOT mean the rule is unused. Consider reviewing its ` +
|
|
36
|
+
`scope and purpose.`,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
return findings;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
/** Whether an artifact has glob-scoped patterns that scoped analysis applies to. */
|
|
43
|
+
export function hasScopedPatterns(artifact) {
|
|
44
|
+
return Boolean(artifact.scope?.patterns?.length);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=scoped-no-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-no-match.js","sourceRoot":"","sources":["../../../src/analyzer/rules/scoped-no-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,CAAC,MAAM,iBAAiB,GAAiB;IAC7C,IAAI,EAAE,iBAAiB;IACvB,GAAG,CAAC,SAAS,EAAE,OAAO;QACpB,2DAA2D;QAC3D,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAClC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;YAC1C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEjD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC3C,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CACrC,CAAC;YACF,IAAI,UAAU;gBAAE,SAAS;YAEzB,QAAQ,CAAC,IAAI,CACX,WAAW,CAAC;gBACV,IAAI,EAAE,iBAAiB;gBACvB,SAAS,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,0CAA0C;gBACjD,WAAW,EACT,mDAAmD,QAAQ,CAAC,IAAI,GAAG;oBACnE,qCAAqC;gBACvC,cAAc,EACZ,gEAAgE;oBAChE,oBAAoB;aACvB,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end analyze runner (Spec §51).
|
|
3
|
+
*
|
|
4
|
+
* Wires the full local-first pipeline:
|
|
5
|
+
* discover -> assess git history -> run analyzer rules -> report
|
|
6
|
+
*
|
|
7
|
+
* No content ever leaves the machine (Spec §36). Git access is only for
|
|
8
|
+
* metadata/sufficiency; it is skipped safely on any error (Spec §34).
|
|
9
|
+
*/
|
|
10
|
+
import { type GitSufficiencyResult } from "../git/sufficiency.js";
|
|
11
|
+
import type { AnalysisReport } from "../types/report.js";
|
|
12
|
+
import { type AnalysisOptions } from "./types.js";
|
|
13
|
+
export interface RunAnalyzeOptions {
|
|
14
|
+
rootPath: string;
|
|
15
|
+
options?: Partial<AnalysisOptions>;
|
|
16
|
+
}
|
|
17
|
+
export interface RunAnalyzeResult {
|
|
18
|
+
report: AnalysisReport;
|
|
19
|
+
git: GitSufficiencyResult;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runs discovery + analysis for a repo root. Never throws on missing git;
|
|
23
|
+
* when history is insufficient, scoped-rule findings are skipped (Rule 18).
|
|
24
|
+
*/
|
|
25
|
+
export declare function runAnalyze(opts: RunAnalyzeOptions): Promise<RunAnalyzeResult>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end analyze runner (Spec §51).
|
|
3
|
+
*
|
|
4
|
+
* Wires the full local-first pipeline:
|
|
5
|
+
* discover -> assess git history -> run analyzer rules -> report
|
|
6
|
+
*
|
|
7
|
+
* No content ever leaves the machine (Spec §36). Git access is only for
|
|
8
|
+
* metadata/sufficiency; it is skipped safely on any error (Spec §34).
|
|
9
|
+
*/
|
|
10
|
+
import { discover } from "../discovery/discover.js";
|
|
11
|
+
import { assessGitHistory, } from "../git/sufficiency.js";
|
|
12
|
+
import { changedFilesInWindow } from "../git/history.js";
|
|
13
|
+
import { analyze } from "./engine.js";
|
|
14
|
+
import { DEFAULT_ANALYSIS_OPTIONS } from "./types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Runs discovery + analysis for a repo root. Never throws on missing git;
|
|
17
|
+
* when history is insufficient, scoped-rule findings are skipped (Rule 18).
|
|
18
|
+
*/
|
|
19
|
+
export async function runAnalyze(opts) {
|
|
20
|
+
const { rootPath } = opts;
|
|
21
|
+
const options = {
|
|
22
|
+
...DEFAULT_ANALYSIS_OPTIONS,
|
|
23
|
+
...opts.options,
|
|
24
|
+
};
|
|
25
|
+
const { artifacts } = await discover(rootPath);
|
|
26
|
+
let git;
|
|
27
|
+
let changedFilePaths;
|
|
28
|
+
try {
|
|
29
|
+
git = await assessGitHistory(rootPath, options.gitWindowDays);
|
|
30
|
+
if (git.sufficient && git.isGitRepo) {
|
|
31
|
+
changedFilePaths = await changedFilesInWindow(rootPath, options.gitWindowDays);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
changedFilePaths = [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Never fail the whole analysis because git is unavailable (Spec §34).
|
|
39
|
+
git = {
|
|
40
|
+
isGitRepo: false,
|
|
41
|
+
isShallow: false,
|
|
42
|
+
windowCommitCount: 0,
|
|
43
|
+
totalCommitCount: 0,
|
|
44
|
+
sufficient: false,
|
|
45
|
+
};
|
|
46
|
+
changedFilePaths = [];
|
|
47
|
+
}
|
|
48
|
+
const report = await analyze({
|
|
49
|
+
artifacts,
|
|
50
|
+
context: {
|
|
51
|
+
rootPath,
|
|
52
|
+
options,
|
|
53
|
+
changedFilePaths,
|
|
54
|
+
gitHistorySufficient: git.sufficient,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return { report, git };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/analyzer/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EACL,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAwB,MAAM,YAAY,CAAC;AAY5E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAuB;IAEvB,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,MAAM,OAAO,GAAoB;QAC/B,GAAG,wBAAwB;QAC3B,GAAG,IAAI,CAAC,OAAO;KAChB,CAAC;IAEF,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE/C,IAAI,GAAyB,CAAC;IAC9B,IAAI,gBAAsC,CAAC;IAE3C,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;QAC9D,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACpC,gBAAgB,GAAG,MAAM,oBAAoB,CAC3C,QAAQ,EACR,OAAO,CAAC,aAAa,CACtB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,gBAAgB,GAAG,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,GAAG,GAAG;YACJ,SAAS,EAAE,KAAK;YAChB,SAAS,EAAE,KAAK;YAChB,iBAAiB,EAAE,CAAC;YACpB,gBAAgB,EAAE,CAAC;YACnB,UAAU,EAAE,KAAK;SAClB,CAAC;QACF,gBAAgB,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;QAC3B,SAAS;QACT,OAAO,EAAE;YACP,QAAQ;YACR,OAAO;YACP,gBAAgB;YAChB,oBAAoB,EAAE,GAAG,CAAC,UAAU;SACrC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity threshold / exit-code semantics for CI (Spec §11, §34; product
|
|
3
|
+
* decision 2026).
|
|
4
|
+
*
|
|
5
|
+
* `--fail-on` consumes a severity level; a finding at or above the threshold
|
|
6
|
+
* causes a non-zero exit. Default (no `--fail-on`) is informational: findings
|
|
7
|
+
* never fail CI on their own.
|
|
8
|
+
*
|
|
9
|
+
* Ranking: info (0) < notice (1) < warning (2).
|
|
10
|
+
*
|
|
11
|
+
* Label→severity mapping (normative, Spec §10):
|
|
12
|
+
* INFO → info, CONTEXT → info, REVIEW → notice, CAUTION → notice.
|
|
13
|
+
*/
|
|
14
|
+
import type { Finding, FindingSeverity } from "../types/finding.js";
|
|
15
|
+
/** Accepted `--fail-on` values (severity level). */
|
|
16
|
+
export type FailThreshold = FindingSeverity;
|
|
17
|
+
/** Whether any finding meets or exceeds the threshold. */
|
|
18
|
+
export declare function shouldFailOnSeverity(findings: Finding[], threshold: FailThreshold): boolean;
|
|
19
|
+
/** Highest severity present in a set of findings, or "info" when empty. */
|
|
20
|
+
export declare function maxSeverity(findings: Finding[]): FindingSeverity;
|
|
21
|
+
export declare function isValidFailThreshold(value: string): value is FailThreshold;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity threshold / exit-code semantics for CI (Spec §11, §34; product
|
|
3
|
+
* decision 2026).
|
|
4
|
+
*
|
|
5
|
+
* `--fail-on` consumes a severity level; a finding at or above the threshold
|
|
6
|
+
* causes a non-zero exit. Default (no `--fail-on`) is informational: findings
|
|
7
|
+
* never fail CI on their own.
|
|
8
|
+
*
|
|
9
|
+
* Ranking: info (0) < notice (1) < warning (2).
|
|
10
|
+
*
|
|
11
|
+
* Label→severity mapping (normative, Spec §10):
|
|
12
|
+
* INFO → info, CONTEXT → info, REVIEW → notice, CAUTION → notice.
|
|
13
|
+
*/
|
|
14
|
+
const SEVERITY_RANK = {
|
|
15
|
+
info: 0,
|
|
16
|
+
notice: 1,
|
|
17
|
+
warning: 2,
|
|
18
|
+
};
|
|
19
|
+
/** Whether any finding meets or exceeds the threshold. */
|
|
20
|
+
export function shouldFailOnSeverity(findings, threshold) {
|
|
21
|
+
const min = SEVERITY_RANK[threshold];
|
|
22
|
+
return findings.some((f) => SEVERITY_RANK[f.severity] >= min);
|
|
23
|
+
}
|
|
24
|
+
/** Highest severity present in a set of findings, or "info" when empty. */
|
|
25
|
+
export function maxSeverity(findings) {
|
|
26
|
+
let rank = 0;
|
|
27
|
+
let sev = "info";
|
|
28
|
+
for (const f of findings) {
|
|
29
|
+
if (SEVERITY_RANK[f.severity] > rank) {
|
|
30
|
+
rank = SEVERITY_RANK[f.severity];
|
|
31
|
+
sev = f.severity;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return sev;
|
|
35
|
+
}
|
|
36
|
+
export function isValidFailThreshold(value) {
|
|
37
|
+
return value === "info" || value === "notice" || value === "warning";
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=severity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity.js","sourceRoot":"","sources":["../../src/analyzer/severity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,aAAa,GAAoC;IACrD,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAClC,QAAmB,EACnB,SAAwB;IAExB,MAAM,GAAG,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,QAAmB;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,GAAG,GAAoB,MAAM,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC;YACrC,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACjC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer contract (Spec §15, §51).
|
|
3
|
+
*
|
|
4
|
+
* Each rule runs independently over the discovered artifacts and returns its
|
|
5
|
+
* own findings. Rules are kept free of presentation concerns (Rule 15) and
|
|
6
|
+
* platform-specific logic lives in adapters (Rule 14).
|
|
7
|
+
*/
|
|
8
|
+
import type { ConfigurationArtifact } from "../types/configuration.js";
|
|
9
|
+
import type { Finding } from "../types/finding.js";
|
|
10
|
+
/** Analysis-wide options, sourced from `.contextcheck.json` (Spec §52). */
|
|
11
|
+
export interface AnalysisOptions {
|
|
12
|
+
/** Files larger than this (estimated tokens) become large-file findings. */
|
|
13
|
+
largeFileTokens: number;
|
|
14
|
+
/** Reserved for future `warning` severity (Spec §20). Not used in MVP. */
|
|
15
|
+
warningFileTokens: number;
|
|
16
|
+
/** Git history window (days) used by scoped-rule analysis (Spec §24, §52). */
|
|
17
|
+
gitWindowDays: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const DEFAULT_ANALYSIS_OPTIONS: AnalysisOptions;
|
|
20
|
+
/** Context handed to every rule (git state, options, repo root). */
|
|
21
|
+
export interface AnalysisContext {
|
|
22
|
+
rootPath: string;
|
|
23
|
+
options: AnalysisOptions;
|
|
24
|
+
/** Paths changed in the Git window, when available. */
|
|
25
|
+
changedFilePaths?: string[];
|
|
26
|
+
/** Whether Git history is sufficient for scoped-rule analysis (Spec §24). */
|
|
27
|
+
gitHistorySufficient?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** A rule producing zero or more findings from the same artifact set. */
|
|
30
|
+
export interface AnalyzerRule {
|
|
31
|
+
name: string;
|
|
32
|
+
run(artifacts: ConfigurationArtifact[], context: AnalysisContext): Promise<Finding[]> | Finding[];
|
|
33
|
+
}
|
|
34
|
+
/** A convenience no-op rule used to keep the pipeline stable while built. */
|
|
35
|
+
export declare function emptyFindings(): Finding[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer contract (Spec §15, §51).
|
|
3
|
+
*
|
|
4
|
+
* Each rule runs independently over the discovered artifacts and returns its
|
|
5
|
+
* own findings. Rules are kept free of presentation concerns (Rule 15) and
|
|
6
|
+
* platform-specific logic lives in adapters (Rule 14).
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_ANALYSIS_OPTIONS = {
|
|
9
|
+
largeFileTokens: 2000,
|
|
10
|
+
warningFileTokens: 4000,
|
|
11
|
+
gitWindowDays: 90,
|
|
12
|
+
};
|
|
13
|
+
/** A convenience no-op rule used to keep the pipeline stable while built. */
|
|
14
|
+
export function emptyFindings() {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/analyzer/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,IAAI;IACvB,aAAa,EAAE,EAAE;CAClB,CAAC;AAqBF,6EAA6E;AAC7E,MAAM,UAAU,aAAa;IAC3B,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* contextcheck CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Wires the local-first pipeline to commands: analyze, snapshot and diff are
|
|
6
|
+
* functional; config remains a stub (handled by `.contextcheck.json`).
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
export declare function analyzeAction(opts: {
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
compact?: boolean;
|
|
13
|
+
failOn?: string;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
/** `contextcheck snapshot` — records current AI configuration + findings. */
|
|
16
|
+
export declare function snapshotAction(): Promise<void>;
|
|
17
|
+
/** `contextcheck diff [snapshot-id]` — compares a snapshot vs current state. */
|
|
18
|
+
export declare function diffAction(from?: string, opts?: {
|
|
19
|
+
json?: boolean;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
/** `contextcheck snapshot list` — lists stored snapshots. */
|
|
22
|
+
export declare function snapshotListAction(): Promise<void>;
|
|
23
|
+
export declare const program: Command;
|