forgeprint 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +41 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +10 -0
- package/dist/bin.js.map +1 -0
- package/dist/build-codeowners.d.ts +8 -0
- package/dist/build-codeowners.d.ts.map +1 -0
- package/dist/build-codeowners.js +26 -0
- package/dist/build-codeowners.js.map +1 -0
- package/dist/build-index.d.ts +45 -0
- package/dist/build-index.d.ts.map +1 -0
- package/dist/build-index.js +41 -0
- package/dist/build-index.js.map +1 -0
- package/dist/build-schema.d.ts +4 -0
- package/dist/build-schema.d.ts.map +1 -0
- package/dist/build-schema.js +38 -0
- package/dist/build-schema.js.map +1 -0
- package/dist/catalog.d.ts +23 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +55 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +30 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +5 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +9 -0
- package/dist/json.js.map +1 -0
- package/dist/lint-setup.d.ts +19 -0
- package/dist/lint-setup.d.ts.map +1 -0
- package/dist/lint-setup.js +278 -0
- package/dist/lint-setup.js.map +1 -0
- package/dist/manifest.d.ts +104 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +93 -0
- package/dist/manifest.js.map +1 -0
- package/dist/options.d.ts +43 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +127 -0
- package/dist/options.js.map +1 -0
- package/dist/paths.d.ts +16 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +37 -0
- package/dist/paths.js.map +1 -0
- package/dist/similarity.d.ts +61 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +184 -0
- package/dist/similarity.js.map +1 -0
- package/dist/taxonomy.d.ts +24 -0
- package/dist/taxonomy.d.ts.map +1 -0
- package/dist/taxonomy.js +46 -0
- package/dist/taxonomy.js.map +1 -0
- package/dist/testing.d.ts +23 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +107 -0
- package/dist/testing.js.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +122 -0
- package/dist/validate.js.map +1 -0
- package/package.json +58 -0
package/dist/options.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving `setup.md` option guards.
|
|
3
|
+
*
|
|
4
|
+
* A recipe is written with every branch present:
|
|
5
|
+
*
|
|
6
|
+
* ```markdown
|
|
7
|
+
* <!-- if options.database == postgres -->
|
|
8
|
+
* ...
|
|
9
|
+
* <!-- endif -->
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* A caller who has chosen `database: postgres` should receive the recipe with
|
|
13
|
+
* that branch inlined and the others gone, because a step list with branches in
|
|
14
|
+
* it is a document, and the point of `setup.md` is that it is a script.
|
|
15
|
+
*/
|
|
16
|
+
const IF_PATTERN = /^\s*<!--\s*if\s+options\.([a-z0-9-]+)\s*==\s*([a-z0-9-]+)\s*-->\s*$/;
|
|
17
|
+
const ENDIF_PATTERN = /^\s*<!--\s*endif\s*-->\s*$/;
|
|
18
|
+
export class UnknownOptionError extends Error {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validate a caller's option selection against what the manifest declares.
|
|
22
|
+
* Throws rather than guessing: a typo that silently selects the default would
|
|
23
|
+
* hand somebody a recipe for a database they did not ask for.
|
|
24
|
+
*/
|
|
25
|
+
export function checkOptions(manifest, chosen) {
|
|
26
|
+
const declared = manifest.options ?? {};
|
|
27
|
+
for (const [field, value] of Object.entries(chosen)) {
|
|
28
|
+
const values = declared[field];
|
|
29
|
+
if (values === undefined) {
|
|
30
|
+
const known = Object.keys(declared);
|
|
31
|
+
throw new UnknownOptionError(known.length === 0
|
|
32
|
+
? `Blueprint "${manifest.slug}" declares no options, but "${field}" was given`
|
|
33
|
+
: `"${field}" is not an option of "${manifest.slug}" (${known.join(', ')})`);
|
|
34
|
+
}
|
|
35
|
+
if (!values.includes(value)) {
|
|
36
|
+
throw new UnknownOptionError(`"${value}" is not a value of ${manifest.slug}.options.${field} (${values.join(', ')})`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Inline the chosen branches and drop the rest.
|
|
42
|
+
*
|
|
43
|
+
* A guard on a field the caller did not choose is left exactly as written, and
|
|
44
|
+
* the field is reported in `unresolved`, so the caller can be asked rather than
|
|
45
|
+
* handed a recipe with a branch silently removed.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveSetupOptions(markdown, chosen = {}) {
|
|
48
|
+
const lines = markdown.split(/\r?\n/);
|
|
49
|
+
const output = [];
|
|
50
|
+
const unresolved = new Set();
|
|
51
|
+
let skipping = false;
|
|
52
|
+
let keepingGuarded = false;
|
|
53
|
+
let inFence = false;
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (/^\s*(?:```|~~~)/.test(line)) {
|
|
56
|
+
inFence = !inFence;
|
|
57
|
+
if (!skipping)
|
|
58
|
+
output.push(line);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (inFence) {
|
|
62
|
+
if (!skipping)
|
|
63
|
+
output.push(line);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const guard = IF_PATTERN.exec(line);
|
|
67
|
+
if (guard !== null) {
|
|
68
|
+
const [, field = '', value = ''] = guard;
|
|
69
|
+
const selection = chosen[field];
|
|
70
|
+
if (selection === undefined) {
|
|
71
|
+
// Not chosen: leave the guard intact and say so.
|
|
72
|
+
unresolved.add(field);
|
|
73
|
+
output.push(line);
|
|
74
|
+
keepingGuarded = false;
|
|
75
|
+
skipping = false;
|
|
76
|
+
}
|
|
77
|
+
else if (selection === value) {
|
|
78
|
+
keepingGuarded = true;
|
|
79
|
+
skipping = false;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
skipping = true;
|
|
83
|
+
keepingGuarded = false;
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (ENDIF_PATTERN.test(line)) {
|
|
88
|
+
if (!skipping && !keepingGuarded)
|
|
89
|
+
output.push(line);
|
|
90
|
+
skipping = false;
|
|
91
|
+
keepingGuarded = false;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (!skipping)
|
|
95
|
+
output.push(line);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
markdown: collapseBlankRuns(output).join('\n'),
|
|
99
|
+
unresolved: [...unresolved].sort(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Removing a branch leaves blank lines behind; two in a row is enough. */
|
|
103
|
+
function collapseBlankRuns(lines) {
|
|
104
|
+
const result = [];
|
|
105
|
+
let blanks = 0;
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (line.trim().length === 0) {
|
|
108
|
+
blanks += 1;
|
|
109
|
+
if (blanks > 1)
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
blanks = 0;
|
|
114
|
+
}
|
|
115
|
+
result.push(line);
|
|
116
|
+
}
|
|
117
|
+
while (result.length > 0 && result[result.length - 1]?.trim().length === 0)
|
|
118
|
+
result.pop();
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
/** Option fields a caller still has to choose, given what they have chosen. */
|
|
122
|
+
export function missingOptions(manifest, chosen = {}) {
|
|
123
|
+
return Object.entries(manifest.options ?? {})
|
|
124
|
+
.filter(([field]) => chosen[field] === undefined)
|
|
125
|
+
.map(([field, values]) => ({ field, values }));
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk upwards from `startDir` until a Forgeprint repository root is found, so
|
|
3
|
+
* that every command works from any subdirectory the way git does.
|
|
4
|
+
*/
|
|
5
|
+
export declare function findRepoRoot(startDir?: string): string;
|
|
6
|
+
export declare const repoPaths: {
|
|
7
|
+
taxonomy: (root: string) => string;
|
|
8
|
+
manifestSchema: (root: string) => string;
|
|
9
|
+
blueprintsDir: (root: string) => string;
|
|
10
|
+
blueprintDir: (root: string, slug: string) => string;
|
|
11
|
+
index: (root: string) => string;
|
|
12
|
+
codeowners: (root: string) => string;
|
|
13
|
+
};
|
|
14
|
+
/** Files every blueprint folder must contain. */
|
|
15
|
+
export declare const REQUIRED_BLUEPRINT_FILES: readonly ["manifest.yaml", "AGENTS.md", "overview.md", "setup.md", "CHANGELOG.md"];
|
|
16
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAYrE;AAED,eAAO,MAAM,SAAS;qBACH,MAAM,KAAG,MAAM;2BACT,MAAM,KAAG,MAAM;0BAChB,MAAM,KAAG,MAAM;yBAChB,MAAM,QAAQ,MAAM,KAAG,MAAM;kBACpC,MAAM,KAAG,MAAM;uBACV,MAAM,KAAG,MAAM;CACnC,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,wBAAwB,oFAM3B,CAAC"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
/** File that marks the root of a Forgeprint repository. */
|
|
4
|
+
const ROOT_MARKER = join('schema', 'taxonomy.yaml');
|
|
5
|
+
/**
|
|
6
|
+
* Walk upwards from `startDir` until a Forgeprint repository root is found, so
|
|
7
|
+
* that every command works from any subdirectory the way git does.
|
|
8
|
+
*/
|
|
9
|
+
export function findRepoRoot(startDir = process.cwd()) {
|
|
10
|
+
let dir = resolve(startDir);
|
|
11
|
+
for (;;) {
|
|
12
|
+
if (existsSync(join(dir, ROOT_MARKER)))
|
|
13
|
+
return dir;
|
|
14
|
+
const parent = dirname(dir);
|
|
15
|
+
if (parent === dir) {
|
|
16
|
+
throw new Error(`Not inside a Forgeprint repository: no ${ROOT_MARKER} found at or above ${resolve(startDir)}`);
|
|
17
|
+
}
|
|
18
|
+
dir = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export const repoPaths = {
|
|
22
|
+
taxonomy: (root) => join(root, 'schema', 'taxonomy.yaml'),
|
|
23
|
+
manifestSchema: (root) => join(root, 'schema', 'manifest.schema.json'),
|
|
24
|
+
blueprintsDir: (root) => join(root, 'blueprints'),
|
|
25
|
+
blueprintDir: (root, slug) => join(root, 'blueprints', slug),
|
|
26
|
+
index: (root) => join(root, 'docs', 'index.json'),
|
|
27
|
+
codeowners: (root) => join(root, '.github', 'CODEOWNERS'),
|
|
28
|
+
};
|
|
29
|
+
/** Files every blueprint folder must contain. */
|
|
30
|
+
export const REQUIRED_BLUEPRINT_FILES = [
|
|
31
|
+
'manifest.yaml',
|
|
32
|
+
'AGENTS.md',
|
|
33
|
+
'overview.md',
|
|
34
|
+
'setup.md',
|
|
35
|
+
'CHANGELOG.md',
|
|
36
|
+
];
|
|
37
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,2DAA2D;AAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAC3D,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,0CAA0C,WAAW,sBAAsB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAC/F,CAAC;QACJ,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,QAAQ,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,CAAC;IACzE,cAAc,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,sBAAsB,CAAC;IACtF,aAAa,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;IACjE,YAAY,EAAE,CAAC,IAAY,EAAE,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC;IACpF,KAAK,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC;IACjE,UAAU,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC;CAC1E,CAAC;AAEF,iDAAiD;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,eAAe;IACf,WAAW;IACX,aAAa;IACb,UAAU;IACV,cAAc;CACN,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate report (rule 10).
|
|
3
|
+
*
|
|
4
|
+
* The catalog is curated, so the question asked of every new blueprint is not
|
|
5
|
+
* "is it good" but "is it already here". This compares one blueprint against
|
|
6
|
+
* every other by tag overlap and by the wording of its `AGENTS.md` and
|
|
7
|
+
* `setup.md`, and names the closest match.
|
|
8
|
+
*/
|
|
9
|
+
import { type Blueprint } from './catalog.js';
|
|
10
|
+
/**
|
|
11
|
+
* What comparing needs: the tag fields, and the text of the two files whose
|
|
12
|
+
* wording gives a copy away. Decoupled from the file system so that the MCP
|
|
13
|
+
* server can compare a contributor's draft it has never seen on disk.
|
|
14
|
+
*/
|
|
15
|
+
export interface SimilaritySubject {
|
|
16
|
+
readonly slug: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly project_type: string;
|
|
19
|
+
readonly stack: readonly string[];
|
|
20
|
+
readonly languages: readonly string[];
|
|
21
|
+
readonly platforms: readonly string[];
|
|
22
|
+
readonly distribution: readonly string[];
|
|
23
|
+
readonly requirements: readonly string[];
|
|
24
|
+
readonly agentsMarkdown: string;
|
|
25
|
+
readonly setupMarkdown: string;
|
|
26
|
+
}
|
|
27
|
+
/** Read a blueprint on disk into a comparable subject. */
|
|
28
|
+
export declare function subjectFromBlueprint(blueprint: Blueprint): SimilaritySubject;
|
|
29
|
+
/** Above this, a reviewer is asked to justify the blueprint's existence. */
|
|
30
|
+
export declare const DEFAULT_THRESHOLD = 0.7;
|
|
31
|
+
export interface SimilarityScore {
|
|
32
|
+
readonly slug: string;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
/** Jaccard overlap of the tag sets, 0–1. */
|
|
35
|
+
readonly tags: number;
|
|
36
|
+
/** Cosine similarity of the TF-IDF vectors of AGENTS.md, 0–1. */
|
|
37
|
+
readonly agents: number;
|
|
38
|
+
/** Cosine similarity of the TF-IDF vectors of setup.md, 0–1. */
|
|
39
|
+
readonly setup: number;
|
|
40
|
+
/** The highest of the three: what the threshold is applied to. */
|
|
41
|
+
readonly highest: number;
|
|
42
|
+
/** Tags only the subject has, and tags only the other one has. */
|
|
43
|
+
readonly onlyHere: readonly string[];
|
|
44
|
+
readonly onlyThere: readonly string[];
|
|
45
|
+
/** True when the two claim the same stack, project type and requirements. */
|
|
46
|
+
readonly sameCombination: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface SimilarityReport {
|
|
49
|
+
readonly slug: string;
|
|
50
|
+
readonly threshold: number;
|
|
51
|
+
/** Every other blueprint, most similar first. */
|
|
52
|
+
readonly scores: readonly SimilarityScore[];
|
|
53
|
+
readonly closest: SimilarityScore | undefined;
|
|
54
|
+
/** True when the closest blueprint is above the threshold. */
|
|
55
|
+
readonly flagged: boolean;
|
|
56
|
+
}
|
|
57
|
+
export declare function compareBlueprints(subject: Blueprint, others: readonly Blueprint[], threshold?: number): SimilarityReport;
|
|
58
|
+
export declare function compareSubjects(subject: SimilaritySubject, others: readonly SimilaritySubject[], threshold?: number): SimilarityReport;
|
|
59
|
+
/** Render the report the way a reviewer reads it. */
|
|
60
|
+
export declare function renderReport(report: SimilarityReport): string;
|
|
61
|
+
//# sourceMappingURL=similarity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.d.ts","sourceRoot":"","sources":["../src/similarity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,GAAG,iBAAiB,CAc5E;AAED,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAC;IAC9C,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,SAAS,GAAE,MAA0B,GACpC,gBAAgB,CAMlB;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,SAAS,GAAE,MAA0B,GACpC,gBAAgB,CAqClB;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CA0C7D"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate report (rule 10).
|
|
3
|
+
*
|
|
4
|
+
* The catalog is curated, so the question asked of every new blueprint is not
|
|
5
|
+
* "is it good" but "is it already here". This compares one blueprint against
|
|
6
|
+
* every other by tag overlap and by the wording of its `AGENTS.md` and
|
|
7
|
+
* `setup.md`, and names the closest match.
|
|
8
|
+
*/
|
|
9
|
+
import { readBlueprintFile } from './catalog.js';
|
|
10
|
+
/** Read a blueprint on disk into a comparable subject. */
|
|
11
|
+
export function subjectFromBlueprint(blueprint) {
|
|
12
|
+
const m = blueprint.manifest;
|
|
13
|
+
return {
|
|
14
|
+
slug: blueprint.slug,
|
|
15
|
+
name: m.name,
|
|
16
|
+
project_type: m.project_type,
|
|
17
|
+
stack: m.stack,
|
|
18
|
+
languages: m.languages,
|
|
19
|
+
platforms: m.platforms,
|
|
20
|
+
distribution: m.distribution,
|
|
21
|
+
requirements: m.requirements,
|
|
22
|
+
agentsMarkdown: fileOf(blueprint, 'AGENTS.md'),
|
|
23
|
+
setupMarkdown: fileOf(blueprint, 'setup.md'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Above this, a reviewer is asked to justify the blueprint's existence. */
|
|
27
|
+
export const DEFAULT_THRESHOLD = 0.7;
|
|
28
|
+
export function compareBlueprints(subject, others, threshold = DEFAULT_THRESHOLD) {
|
|
29
|
+
return compareSubjects(subjectFromBlueprint(subject), others.map(subjectFromBlueprint), threshold);
|
|
30
|
+
}
|
|
31
|
+
export function compareSubjects(subject, others, threshold = DEFAULT_THRESHOLD) {
|
|
32
|
+
const corpus = [subject, ...others];
|
|
33
|
+
const agentsIdf = inverseDocumentFrequency(corpus.map((b) => tokens(b.agentsMarkdown)));
|
|
34
|
+
const setupIdf = inverseDocumentFrequency(corpus.map((b) => tokens(b.setupMarkdown)));
|
|
35
|
+
const subjectAgents = vector(tokens(subject.agentsMarkdown), agentsIdf);
|
|
36
|
+
const subjectSetup = vector(tokens(subject.setupMarkdown), setupIdf);
|
|
37
|
+
const subjectTags = tagSet(subject);
|
|
38
|
+
const scores = others
|
|
39
|
+
.map((other) => {
|
|
40
|
+
const otherTags = tagSet(other);
|
|
41
|
+
const tags = jaccard(subjectTags, otherTags);
|
|
42
|
+
const agents = cosine(subjectAgents, vector(tokens(other.agentsMarkdown), agentsIdf));
|
|
43
|
+
const setup = cosine(subjectSetup, vector(tokens(other.setupMarkdown), setupIdf));
|
|
44
|
+
return {
|
|
45
|
+
slug: other.slug,
|
|
46
|
+
name: other.name,
|
|
47
|
+
tags,
|
|
48
|
+
agents,
|
|
49
|
+
setup,
|
|
50
|
+
highest: Math.max(tags, agents, setup),
|
|
51
|
+
onlyHere: [...subjectTags].filter((tag) => !otherTags.has(tag)).sort(),
|
|
52
|
+
onlyThere: [...otherTags].filter((tag) => !subjectTags.has(tag)).sort(),
|
|
53
|
+
sameCombination: combination(subject) === combination(other),
|
|
54
|
+
};
|
|
55
|
+
})
|
|
56
|
+
.sort((a, b) => b.highest - a.highest || a.slug.localeCompare(b.slug));
|
|
57
|
+
const closest = scores[0];
|
|
58
|
+
return {
|
|
59
|
+
slug: subject.slug,
|
|
60
|
+
threshold,
|
|
61
|
+
scores,
|
|
62
|
+
closest,
|
|
63
|
+
flagged: closest !== undefined && closest.highest > threshold,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Render the report the way a reviewer reads it. */
|
|
67
|
+
export function renderReport(report) {
|
|
68
|
+
const lines = [`similarity report for ${report.slug}`];
|
|
69
|
+
if (report.scores.length === 0) {
|
|
70
|
+
lines.push(' no other blueprint in the catalog to compare against');
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
lines.push('');
|
|
74
|
+
lines.push(' blueprint tags AGENTS.md setup.md');
|
|
75
|
+
for (const score of report.scores) {
|
|
76
|
+
lines.push(` ${score.slug.padEnd(30)} ${percent(score.tags)} ${percent(score.agents).padStart(9)} ${percent(score.setup).padStart(8)}`);
|
|
77
|
+
}
|
|
78
|
+
const closest = report.closest;
|
|
79
|
+
if (closest === undefined)
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(` closest: ${closest.slug} (${closest.name})`);
|
|
83
|
+
lines.push(` only in ${report.slug}: ${closest.onlyHere.join(', ') || '(nothing)'}`);
|
|
84
|
+
lines.push(` only in ${closest.slug}: ${closest.onlyThere.join(', ') || '(nothing)'}`);
|
|
85
|
+
if (closest.sameCombination) {
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push(` REJECTED: ${closest.slug} claims the same stack + project_type + requirements (rule 9).`);
|
|
88
|
+
lines.push(' Improve it, add an option to it, or supersede it.');
|
|
89
|
+
}
|
|
90
|
+
else if (report.flagged) {
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(` RED FLAG: ${percent(closest.highest)} similar to ${closest.slug}, over the ${percent(report.threshold)} threshold.`);
|
|
93
|
+
lines.push(' State in the pull request what is different and why this is not a');
|
|
94
|
+
lines.push(' change to that blueprint instead.');
|
|
95
|
+
}
|
|
96
|
+
return lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
function percent(value) {
|
|
99
|
+
return `${(value * 100).toFixed(0).padStart(3)}%`;
|
|
100
|
+
}
|
|
101
|
+
function fileOf(blueprint, file) {
|
|
102
|
+
return blueprint.files.includes(file) ? readBlueprintFile(blueprint, file) : '';
|
|
103
|
+
}
|
|
104
|
+
function combination(subject) {
|
|
105
|
+
return [
|
|
106
|
+
[...subject.stack].sort().join(','),
|
|
107
|
+
subject.project_type,
|
|
108
|
+
[...subject.requirements].sort().join(','),
|
|
109
|
+
].join('|');
|
|
110
|
+
}
|
|
111
|
+
/** Every tag, namespaced so that `docker` as a platform and as a stack differ. */
|
|
112
|
+
function tagSet(subject) {
|
|
113
|
+
const tags = new Set([`type:${subject.project_type}`]);
|
|
114
|
+
const add = (prefix, values) => {
|
|
115
|
+
for (const value of values)
|
|
116
|
+
tags.add(`${prefix}:${value}`);
|
|
117
|
+
};
|
|
118
|
+
add('stack', subject.stack);
|
|
119
|
+
add('lang', subject.languages);
|
|
120
|
+
add('platform', subject.platforms);
|
|
121
|
+
add('dist', subject.distribution);
|
|
122
|
+
add('req', subject.requirements);
|
|
123
|
+
return tags;
|
|
124
|
+
}
|
|
125
|
+
function jaccard(a, b) {
|
|
126
|
+
if (a.size === 0 && b.size === 0)
|
|
127
|
+
return 0;
|
|
128
|
+
let shared = 0;
|
|
129
|
+
for (const value of a)
|
|
130
|
+
if (b.has(value))
|
|
131
|
+
shared += 1;
|
|
132
|
+
return shared / (a.size + b.size - shared);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Words worth comparing: markdown syntax, code punctuation and the most common
|
|
136
|
+
* English filler are dropped, because every blueprint contains them.
|
|
137
|
+
*/
|
|
138
|
+
const STOPWORDS = new Set(('a an the and or but if then than that this these those is are was were be been being ' +
|
|
139
|
+
'to of in on at for with from by as it its use uses using you your we our they them ' +
|
|
140
|
+
'not no yes do does done can may will shall should would could there here when while ' +
|
|
141
|
+
'each every any all one two some more most other same such only also just into out up ' +
|
|
142
|
+
'down over under again further once about after before between during').split(' '));
|
|
143
|
+
function tokens(text) {
|
|
144
|
+
return text
|
|
145
|
+
.toLowerCase()
|
|
146
|
+
.split(/[^a-z0-9.+#-]+/)
|
|
147
|
+
.map((token) => token.replace(/^[.+#-]+|[.+#-]+$/g, ''))
|
|
148
|
+
.filter((token) => token.length > 2 && !STOPWORDS.has(token));
|
|
149
|
+
}
|
|
150
|
+
function inverseDocumentFrequency(documents) {
|
|
151
|
+
const count = new Map();
|
|
152
|
+
for (const document of documents) {
|
|
153
|
+
for (const token of new Set(document)) {
|
|
154
|
+
count.set(token, (count.get(token) ?? 0) + 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const idf = new Map();
|
|
158
|
+
const total = documents.length;
|
|
159
|
+
for (const [token, seen] of count) {
|
|
160
|
+
idf.set(token, Math.log((total + 1) / (seen + 1)) + 1);
|
|
161
|
+
}
|
|
162
|
+
return idf;
|
|
163
|
+
}
|
|
164
|
+
function vector(document, idf) {
|
|
165
|
+
const frequency = new Map();
|
|
166
|
+
for (const token of document)
|
|
167
|
+
frequency.set(token, (frequency.get(token) ?? 0) + 1);
|
|
168
|
+
const weighted = new Map();
|
|
169
|
+
for (const [token, count] of frequency) {
|
|
170
|
+
weighted.set(token, (count / document.length) * (idf.get(token) ?? 1));
|
|
171
|
+
}
|
|
172
|
+
return weighted;
|
|
173
|
+
}
|
|
174
|
+
function cosine(a, b) {
|
|
175
|
+
if (a.size === 0 || b.size === 0)
|
|
176
|
+
return 0;
|
|
177
|
+
let dot = 0;
|
|
178
|
+
for (const [token, weight] of a)
|
|
179
|
+
dot += weight * (b.get(token) ?? 0);
|
|
180
|
+
const magnitude = (vec) => Math.sqrt([...vec.values()].reduce((sum, value) => sum + value * value, 0));
|
|
181
|
+
const scale = magnitude(a) * magnitude(b);
|
|
182
|
+
return scale === 0 ? 0 : Math.min(1, dot / scale);
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=similarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.js","sourceRoot":"","sources":["../src/similarity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Identifiers used in manifests: lower kebab-case, ASCII only. */
|
|
3
|
+
export declare const SLUG_PATTERN: RegExp;
|
|
4
|
+
export declare const taxonomySchema: z.ZodObject<{
|
|
5
|
+
version: z.ZodLiteral<1>;
|
|
6
|
+
languages: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
7
|
+
stack: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
8
|
+
platforms: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
9
|
+
distribution: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
10
|
+
project_type: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
11
|
+
audience: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
12
|
+
requirements: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
13
|
+
agents: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
14
|
+
tier: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type Taxonomy = z.infer<typeof taxonomySchema>;
|
|
17
|
+
/** Vocabulary names a manifest field can draw from. */
|
|
18
|
+
export type Vocabulary = Exclude<keyof Taxonomy, 'version'>;
|
|
19
|
+
export declare function parseTaxonomy(source: string): Taxonomy;
|
|
20
|
+
export declare function loadTaxonomy(root: string): Taxonomy;
|
|
21
|
+
/** Sorted identifiers of one vocabulary. Sorted so generated output is stable. */
|
|
22
|
+
export declare function terms(taxonomy: Taxonomy, vocabulary: Vocabulary): string[];
|
|
23
|
+
export declare function describeError(error: unknown): string;
|
|
24
|
+
//# sourceMappingURL=taxonomy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomy.d.ts","sourceRoot":"","sources":["../src/taxonomy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,mEAAmE;AACnE,eAAO,MAAM,YAAY,QAA+B,CAAC;AAIzD,eAAO,MAAM,cAAc;;;;;;;;;;;kBAahB,CAAC;AAEZ,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,QAAQ,EAAE,SAAS,CAAC,CAAC;AAE5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAEtD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAOnD;AAED,kFAAkF;AAClF,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE,CAE1E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOpD"}
|
package/dist/taxonomy.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { parse } from 'yaml';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { repoPaths } from './paths.js';
|
|
5
|
+
/** Identifiers used in manifests: lower kebab-case, ASCII only. */
|
|
6
|
+
export const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7
|
+
const termMap = z.record(z.string().regex(SLUG_PATTERN), z.string().min(1));
|
|
8
|
+
export const taxonomySchema = z
|
|
9
|
+
.object({
|
|
10
|
+
version: z.literal(1),
|
|
11
|
+
languages: termMap,
|
|
12
|
+
stack: termMap,
|
|
13
|
+
platforms: termMap,
|
|
14
|
+
distribution: termMap,
|
|
15
|
+
project_type: termMap,
|
|
16
|
+
audience: termMap,
|
|
17
|
+
requirements: termMap,
|
|
18
|
+
agents: termMap,
|
|
19
|
+
tier: termMap,
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
export function parseTaxonomy(source) {
|
|
23
|
+
return taxonomySchema.parse(parse(source));
|
|
24
|
+
}
|
|
25
|
+
export function loadTaxonomy(root) {
|
|
26
|
+
const file = repoPaths.taxonomy(root);
|
|
27
|
+
try {
|
|
28
|
+
return parseTaxonomy(readFileSync(file, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new Error(`Invalid taxonomy at ${file}: ${describeError(error)}`, { cause: error });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Sorted identifiers of one vocabulary. Sorted so generated output is stable. */
|
|
35
|
+
export function terms(taxonomy, vocabulary) {
|
|
36
|
+
return Object.keys(taxonomy[vocabulary]).sort();
|
|
37
|
+
}
|
|
38
|
+
export function describeError(error) {
|
|
39
|
+
if (error instanceof z.ZodError) {
|
|
40
|
+
return error.issues
|
|
41
|
+
.map((issue) => `${issue.path.join('.') || '<root>'}: ${issue.message}`)
|
|
42
|
+
.join('; ');
|
|
43
|
+
}
|
|
44
|
+
return error instanceof Error ? error.message : String(error);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=taxonomy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomy.js","sourceRoot":"","sources":["../src/taxonomy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,mEAAmE;AACnE,MAAM,CAAC,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAEzD,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC5B,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,SAAS,EAAE,OAAO;IAClB,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,OAAO;IAClB,YAAY,EAAE,OAAO;IACrB,YAAY,EAAE,OAAO;IACrB,QAAQ,EAAE,OAAO;IACjB,YAAY,EAAE,OAAO;IACrB,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,OAAO;CACd,CAAC;KACD,MAAM,EAAE,CAAC;AAOZ,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,KAAK,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,KAAK,CAAC,QAAkB,EAAE,UAAsB;IAC9D,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,MAAM;aAChB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aACvE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A taxonomy small enough to read in a test failure message. */
|
|
2
|
+
export declare const TEST_TAXONOMY = "\nversion: 1\nlanguages:\n csharp: C#\n typescript: TypeScript\nstack:\n aspnetcore: ASP.NET Core\n postgres: PostgreSQL\n node: Node.js\nplatforms:\n linux: Linux\n docker: Docker\ndistribution:\n saas: SaaS\n free: Free\nproject_type:\n api: API service\n cli: Command-line tool\naudience:\n intermediate: Intermediate\nrequirements:\n auth: Authentication\n ci: Continuous integration\nagents:\n claude-code: Claude Code\ntier:\n community: Community\n official: Official\n";
|
|
3
|
+
export interface ManifestFields {
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
/** A manifest that passes validation, so a test only states what it changes. */
|
|
7
|
+
export declare function validManifest(overrides?: ManifestFields): ManifestFields;
|
|
8
|
+
export interface BlueprintFixture {
|
|
9
|
+
readonly slug: string;
|
|
10
|
+
readonly manifest?: ManifestFields | null;
|
|
11
|
+
/** Extra or replacement files, keyed by path relative to the folder. */
|
|
12
|
+
readonly files?: Record<string, string>;
|
|
13
|
+
/** Required files to leave out, to exercise the missing-file check. */
|
|
14
|
+
readonly omit?: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a throwaway repository on disk. Generated artifacts are written too,
|
|
18
|
+
* so that `validateCatalog` starts from a clean state.
|
|
19
|
+
*/
|
|
20
|
+
export declare function makeRepo(blueprints?: readonly BlueprintFixture[]): string;
|
|
21
|
+
/** Write the generated artifacts so drift checks pass. */
|
|
22
|
+
export declare function regenerate(root: string): void;
|
|
23
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAUA,iEAAiE;AACjE,eAAO,MAAM,aAAa,kfA4BzB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,SAAS,GAAE,cAAmB,GAAG,cAAc,CAmB5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,UAAU,GAAE,SAAS,gBAAgB,EAAO,GAAG,MAAM,CA8B7E;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAW7C"}
|