@graftsync/spec 0.0.0-stage → 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brandon Roberts
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,45 @@
1
+ export interface SplitDocument {
2
+ /** Raw YAML between the fences (no fences). */
3
+ frontmatter: string;
4
+ /** Everything after the closing fence. */
5
+ body: string;
6
+ /** Line number (1-based) where the body starts. */
7
+ bodyLine: number;
8
+ }
9
+ /** Split a graft.md file into frontmatter YAML and markdown body. */
10
+ export declare function splitFrontmatter(text: string): SplitDocument | null;
11
+ export declare function joinFrontmatter(frontmatter: string, body: string): string;
12
+ export interface Section {
13
+ /** Heading text, trimmed. */
14
+ title: string;
15
+ /** Section content (without the heading line), trimmed. */
16
+ content: string;
17
+ /** 1-based line of the heading within the body. */
18
+ line: number;
19
+ }
20
+ /** Extract level-2 (`## `) sections from a markdown body. Ignores headings inside code fences. */
21
+ export declare function parseSections(body: string): Section[];
22
+ export declare function findSection(sections: Section[], title: string): Section | undefined;
23
+ /**
24
+ * Comment- and formatting-preserving editor for graft.md.
25
+ * Used for bookkeeping (status, verified version, variants, history) so that
26
+ * hand-written YAML comments and the agent-facing body survive tool edits.
27
+ */
28
+ export declare class GraftDocumentEditor {
29
+ private doc;
30
+ private body;
31
+ constructor(text: string);
32
+ get(path: (string | number)[]): unknown;
33
+ set(path: (string | number)[], value: unknown): this;
34
+ setStatus(status: string): this;
35
+ setVerified(version: string): this;
36
+ /** Append a variant entry. Existing entries are left untouched except where `rangeUpdates` says so. */
37
+ addVariant(variant: {
38
+ range: string;
39
+ module: string;
40
+ }, rangeUpdates?: Map<number, string>): this;
41
+ /** Append a bullet to the `## History` section, creating it if missing. */
42
+ appendHistory(entry: string, date?: string): this;
43
+ toString(): string;
44
+ }
45
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAWnE;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED,MAAM,WAAW,OAAO;IACtB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAwBrD;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAGnF;AAED;;;;GAIG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,IAAI,CAAS;IAErB,YAAY,IAAI,EAAE,MAAM,EAMvB;IAED,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAGtC;IAED,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGnD;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjC;IAED,uGAAuG;IACvG,UAAU,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,YAAY,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,IAAI,CAU1G;IAED,2EAA2E;IAC3E,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAA8C,GAAG,IAAI,CAoCvF;IAED,QAAQ,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,145 @@
1
+ import { parseDocument } from 'yaml';
2
+ const FENCE = '---';
3
+ /** Split a graft.md file into frontmatter YAML and markdown body. */
4
+ export function splitFrontmatter(text) {
5
+ const normalized = text.replace(/^/, '');
6
+ const lines = normalized.split(/\r?\n/);
7
+ if (lines[0]?.trim() !== FENCE)
8
+ return null;
9
+ const close = lines.findIndex((line, i) => i > 0 && line.trim() === FENCE);
10
+ if (close === -1)
11
+ return null;
12
+ return {
13
+ frontmatter: lines.slice(1, close).join('\n'),
14
+ body: lines.slice(close + 1).join('\n'),
15
+ bodyLine: close + 2,
16
+ };
17
+ }
18
+ export function joinFrontmatter(frontmatter, body) {
19
+ const fm = frontmatter.endsWith('\n') ? frontmatter : `${frontmatter}\n`;
20
+ return `${FENCE}\n${fm}${FENCE}\n${body}`;
21
+ }
22
+ /** Extract level-2 (`## `) sections from a markdown body. Ignores headings inside code fences. */
23
+ export function parseSections(body) {
24
+ const lines = body.split(/\r?\n/);
25
+ const sections = [];
26
+ let current = null;
27
+ let inFence = false;
28
+ const flush = (end) => {
29
+ if (!current)
30
+ return;
31
+ sections.push({
32
+ title: current.title,
33
+ line: current.line,
34
+ content: lines.slice(current.start, end).join('\n').trim(),
35
+ });
36
+ };
37
+ lines.forEach((line, i) => {
38
+ if (/^\s*(```|~~~)/.test(line))
39
+ inFence = !inFence;
40
+ if (inFence)
41
+ return;
42
+ const m = /^##\s+(.+?)\s*#*\s*$/.exec(line);
43
+ if (m && !line.startsWith('###')) {
44
+ flush(i);
45
+ current = { title: m[1], line: i + 1, start: i + 1 };
46
+ }
47
+ });
48
+ flush(lines.length);
49
+ return sections;
50
+ }
51
+ export function findSection(sections, title) {
52
+ const wanted = title.toLowerCase();
53
+ return sections.find((s) => s.title.toLowerCase() === wanted);
54
+ }
55
+ /**
56
+ * Comment- and formatting-preserving editor for graft.md.
57
+ * Used for bookkeeping (status, verified version, variants, history) so that
58
+ * hand-written YAML comments and the agent-facing body survive tool edits.
59
+ */
60
+ export class GraftDocumentEditor {
61
+ doc;
62
+ body;
63
+ constructor(text) {
64
+ const split = splitFrontmatter(text);
65
+ if (!split)
66
+ throw new Error('graft.md has no YAML frontmatter');
67
+ this.doc = parseDocument(split.frontmatter);
68
+ if (this.doc.errors.length)
69
+ throw new Error(`invalid frontmatter YAML: ${this.doc.errors[0].message}`);
70
+ this.body = split.body;
71
+ }
72
+ get(path) {
73
+ const v = this.doc.getIn(path);
74
+ return v && typeof v === 'object' && 'toJSON' in v ? v.toJSON() : v;
75
+ }
76
+ set(path, value) {
77
+ this.doc.setIn(path, value);
78
+ return this;
79
+ }
80
+ setStatus(status) {
81
+ return this.set(['status'], status);
82
+ }
83
+ setVerified(version) {
84
+ return this.set(['rootstock', 'verified'], version);
85
+ }
86
+ /** Append a variant entry. Existing entries are left untouched except where `rangeUpdates` says so. */
87
+ addVariant(variant, rangeUpdates = new Map()) {
88
+ for (const [index, range] of rangeUpdates)
89
+ this.doc.setIn(['variants', index, 'range'], range);
90
+ const node = this.doc.createNode({ range: variant.range, module: variant.module });
91
+ const seq = this.doc.getIn(['variants']);
92
+ if (seq && typeof seq === 'object' && 'add' in seq) {
93
+ seq.add(node);
94
+ }
95
+ else {
96
+ this.doc.setIn(['variants'], [variant]);
97
+ }
98
+ return this;
99
+ }
100
+ /** Append a bullet to the `## History` section, creating it if missing. */
101
+ appendHistory(entry, date = new Date().toISOString().slice(0, 10)) {
102
+ const bullet = `- ${date}: ${entry.trim().replace(/\n+/g, ' ')}`;
103
+ const lines = this.body.split('\n');
104
+ let start = -1;
105
+ let inFence = false;
106
+ for (let i = 0; i < lines.length; i++) {
107
+ const line = lines[i];
108
+ if (/^\s*(```|~~~)/.test(line))
109
+ inFence = !inFence;
110
+ if (!inFence && /^##\s+history\s*$/i.test(line.trim())) {
111
+ start = i;
112
+ break;
113
+ }
114
+ }
115
+ if (start === -1) {
116
+ const trimmed = this.body.replace(/\s*$/, '');
117
+ this.body = `${trimmed}\n\n## History\n\n${bullet}\n`;
118
+ return this;
119
+ }
120
+ // Find end of the History section: next level-1/2 heading (outside code fences).
121
+ let end = lines.length;
122
+ inFence = false;
123
+ for (let i = start + 1; i < lines.length; i++) {
124
+ const line = lines[i];
125
+ if (/^\s*(```|~~~)/.test(line))
126
+ inFence = !inFence;
127
+ if (!inFence && /^#{1,2}\s/.test(line)) {
128
+ end = i;
129
+ break;
130
+ }
131
+ }
132
+ // Insert after the last non-blank line of the section.
133
+ let insertAt = end;
134
+ while (insertAt > start + 1 && lines[insertAt - 1].trim() === '')
135
+ insertAt--;
136
+ const needsBlank = insertAt === start + 1;
137
+ lines.splice(insertAt, 0, ...(needsBlank ? ['', bullet] : [bullet]));
138
+ this.body = lines.join('\n');
139
+ return this;
140
+ }
141
+ toString() {
142
+ return joinFrontmatter(this.doc.toString(), this.body);
143
+ }
144
+ }
145
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './schema.js';
2
+ export * from './document.js';
3
+ export * from './parse.js';
4
+ export * from './template.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './schema.js';
2
+ export * from './document.js';
3
+ export * from './parse.js';
4
+ export * from './template.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type Section } from './document.js';
2
+ import { type GraftFrontmatter } from './schema.js';
3
+ export type DiagnosticSeverity = 'error' | 'warning';
4
+ export interface Diagnostic {
5
+ severity: DiagnosticSeverity;
6
+ /** Stable machine-readable code, e.g. `frontmatter/invalid`, `body/missing-section`. */
7
+ code: string;
8
+ message: string;
9
+ /** Dotted frontmatter path or section name the diagnostic refers to. */
10
+ path?: string;
11
+ }
12
+ export interface ParsedGraft {
13
+ frontmatter: GraftFrontmatter;
14
+ body: string;
15
+ sections: Section[];
16
+ }
17
+ export interface ParseResult {
18
+ /** Present whenever frontmatter passed schema validation (even if other errors exist). */
19
+ graft?: ParsedGraft;
20
+ diagnostics: Diagnostic[];
21
+ /** True when there are no error-severity diagnostics. */
22
+ ok: boolean;
23
+ }
24
+ export interface ParseOptions {
25
+ /** Name of the directory containing graft.md; must equal `id` when given. */
26
+ directoryName?: string;
27
+ }
28
+ /** Parse and validate the text of a graft.md file. Never throws on invalid input. */
29
+ export declare function parseGraft(text: string, options?: ParseOptions): ParseResult;
30
+ /** Return the variant whose range contains `version` (prereleases included), if any. */
31
+ export declare function selectVariant<V extends {
32
+ range: string;
33
+ }>(variants: readonly V[], version: string): V | undefined;
34
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgD,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAA6C,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/F,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,0FAA0F;IAC1F,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,yDAAyD;IACzD,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,WAAW,CAqFhF;AAwCD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAEjH"}
package/dist/parse.js ADDED
@@ -0,0 +1,128 @@
1
+ import semver from 'semver';
2
+ import { parse as parseYaml } from 'yaml';
3
+ import { parseSections, splitFrontmatter, findSection } from './document.js';
4
+ import { GraftFrontmatterSchema, REQUIRED_SECTIONS } from './schema.js';
5
+ /** Parse and validate the text of a graft.md file. Never throws on invalid input. */
6
+ export function parseGraft(text, options = {}) {
7
+ const diagnostics = [];
8
+ const done = (graft) => ({
9
+ graft,
10
+ diagnostics,
11
+ ok: !diagnostics.some((d) => d.severity === 'error'),
12
+ });
13
+ const split = splitFrontmatter(text);
14
+ if (!split) {
15
+ diagnostics.push({
16
+ severity: 'error',
17
+ code: 'frontmatter/missing',
18
+ message: 'graft.md must start with YAML frontmatter delimited by `---` lines',
19
+ });
20
+ return done();
21
+ }
22
+ let raw;
23
+ try {
24
+ raw = parseYaml(split.frontmatter);
25
+ }
26
+ catch (err) {
27
+ diagnostics.push({
28
+ severity: 'error',
29
+ code: 'frontmatter/yaml',
30
+ message: `frontmatter is not valid YAML: ${err.message}`,
31
+ });
32
+ return done();
33
+ }
34
+ const result = GraftFrontmatterSchema.safeParse(raw ?? {});
35
+ if (!result.success) {
36
+ for (const issue of result.error.issues) {
37
+ diagnostics.push({
38
+ severity: 'error',
39
+ code: 'frontmatter/invalid',
40
+ path: issue.path.join('.'),
41
+ message: `${issue.path.join('.') || '(root)'}: ${issue.message}`,
42
+ });
43
+ }
44
+ }
45
+ const sections = parseSections(split.body);
46
+ for (const name of REQUIRED_SECTIONS) {
47
+ const section = findSection(sections, name);
48
+ if (!section) {
49
+ diagnostics.push({
50
+ severity: 'error',
51
+ code: 'body/missing-section',
52
+ path: name,
53
+ message: `missing required section "## ${name}"`,
54
+ });
55
+ }
56
+ else if (!section.content) {
57
+ diagnostics.push({
58
+ severity: 'warning',
59
+ code: 'body/empty-section',
60
+ path: name,
61
+ message: `section "## ${name}" is empty; agents rely on it during sync`,
62
+ });
63
+ }
64
+ }
65
+ if (!result.success)
66
+ return done();
67
+ const fm = result.data;
68
+ if (options.directoryName !== undefined && options.directoryName !== fm.id) {
69
+ diagnostics.push({
70
+ severity: 'error',
71
+ code: 'frontmatter/id-mismatch',
72
+ path: 'id',
73
+ message: `id "${fm.id}" must match its directory name "${options.directoryName}"`,
74
+ });
75
+ }
76
+ diagnostics.push(...checkVariantRanges(fm));
77
+ if (fm.mechanism === 'fork' && !fm.fork) {
78
+ diagnostics.push({
79
+ severity: 'error',
80
+ code: 'frontmatter/fork-missing',
81
+ path: 'fork',
82
+ message: 'mechanism "fork" needs a `fork:` block ({ repo, upstream, tag })',
83
+ });
84
+ }
85
+ return done({ frontmatter: fm, body: split.body, sections });
86
+ }
87
+ function checkVariantRanges(fm) {
88
+ const out = [];
89
+ const opts = { includePrerelease: true };
90
+ fm.variants.forEach((a, i) => {
91
+ fm.variants.slice(i + 1).forEach((b, j) => {
92
+ if (semver.intersects(a.range, b.range, opts)) {
93
+ out.push({
94
+ severity: 'error',
95
+ code: 'variants/overlap',
96
+ path: `variants.${i}`,
97
+ message: `variant ranges "${a.range}" (variants.${i}) and "${b.range}" (variants.${i + j + 1}) overlap; a version must select exactly one variant`,
98
+ });
99
+ }
100
+ });
101
+ });
102
+ const modules = new Set();
103
+ fm.variants.forEach((v, i) => {
104
+ if (modules.has(v.module)) {
105
+ out.push({
106
+ severity: 'error',
107
+ code: 'variants/duplicate-module',
108
+ path: `variants.${i}.module`,
109
+ message: `variant module "${v.module}" is listed more than once`,
110
+ });
111
+ }
112
+ modules.add(v.module);
113
+ });
114
+ if (!fm.variants.some((v) => semver.satisfies(fm.rootstock.verified, v.range, opts))) {
115
+ out.push({
116
+ severity: 'warning',
117
+ code: 'variants/verified-uncovered',
118
+ path: 'rootstock.verified',
119
+ message: `verified version ${fm.rootstock.verified} is not covered by any variant range`,
120
+ });
121
+ }
122
+ return out;
123
+ }
124
+ /** Return the variant whose range contains `version` (prereleases included), if any. */
125
+ export function selectVariant(variants, version) {
126
+ return variants.find((v) => semver.satisfies(version, v.range, { includePrerelease: true }));
127
+ }
128
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod';
2
+ /** Lifecycle of a graft. */
3
+ export declare const GRAFT_STATUSES: readonly ['applied', 'conflicted', 'upstreamed', 'obsolete'];
4
+ export type GraftStatus = (typeof GRAFT_STATUSES)[number];
5
+ /** Statuses for which tooling still applies and verifies the graft. */
6
+ export declare const ACTIVE_STATUSES: readonly GraftStatus[];
7
+ /** Body sections every graft.md must contain, in recommended order. */
8
+ export declare const REQUIRED_SECTIONS: readonly ['Why', 'Invariants', 'Where to look', 'Anchors', 'History'];
9
+ export type RequiredSection = (typeof REQUIRED_SECTIONS)[number];
10
+ export declare const GRAFT_ID_PATTERN: RegExp;
11
+ export declare const RootstockSchema: z.ZodObject<{
12
+ package: z.ZodString;
13
+ verified: z.ZodString;
14
+ }, z.core.$strip>;
15
+ export declare const VariantRefSchema: z.ZodObject<{
16
+ range: z.ZodString;
17
+ module: z.ZodString;
18
+ }, z.core.$strip>;
19
+ export declare const OracleSchema: z.ZodObject<{
20
+ command: z.ZodString;
21
+ modes: z.ZodDefault<z.ZodArray<z.ZodString>>;
22
+ timeout: z.ZodOptional<z.ZodNumber>;
23
+ }, z.core.$strip>;
24
+ export declare const ForkSchema: z.ZodObject<{
25
+ repo: z.ZodString;
26
+ upstream: z.ZodString;
27
+ tag: z.ZodDefault<z.ZodString>;
28
+ track: z.ZodOptional<z.ZodString>;
29
+ dir: z.ZodOptional<z.ZodString>;
30
+ install: z.ZodDefault<z.ZodEnum<{
31
+ none: "none";
32
+ package: "package";
33
+ }>>;
34
+ build: z.ZodOptional<z.ZodString>;
35
+ timeout: z.ZodOptional<z.ZodNumber>;
36
+ }, z.core.$strip>;
37
+ export declare const GeneratorSchema: z.ZodObject<{
38
+ command: z.ZodString;
39
+ timeout: z.ZodOptional<z.ZodNumber>;
40
+ }, z.core.$strip>;
41
+ export declare const GraftFrontmatterSchema: z.ZodObject<{
42
+ id: z.ZodString;
43
+ rootstock: z.ZodObject<{
44
+ package: z.ZodString;
45
+ verified: z.ZodString;
46
+ }, z.core.$strip>;
47
+ mechanism: z.ZodString;
48
+ targets: z.ZodOptional<z.ZodArray<z.ZodString>>;
49
+ variants: z.ZodArray<z.ZodObject<{
50
+ range: z.ZodString;
51
+ module: z.ZodString;
52
+ }, z.core.$strip>>;
53
+ oracle: z.ZodObject<{
54
+ command: z.ZodString;
55
+ modes: z.ZodDefault<z.ZodArray<z.ZodString>>;
56
+ timeout: z.ZodOptional<z.ZodNumber>;
57
+ }, z.core.$strip>;
58
+ generator: z.ZodOptional<z.ZodObject<{
59
+ command: z.ZodString;
60
+ timeout: z.ZodOptional<z.ZodNumber>;
61
+ }, z.core.$strip>>;
62
+ fork: z.ZodOptional<z.ZodObject<{
63
+ repo: z.ZodString;
64
+ upstream: z.ZodString;
65
+ tag: z.ZodDefault<z.ZodString>;
66
+ track: z.ZodOptional<z.ZodString>;
67
+ dir: z.ZodOptional<z.ZodString>;
68
+ install: z.ZodDefault<z.ZodEnum<{
69
+ none: "none";
70
+ package: "package";
71
+ }>>;
72
+ build: z.ZodOptional<z.ZodString>;
73
+ timeout: z.ZodOptional<z.ZodNumber>;
74
+ }, z.core.$strip>>;
75
+ status: z.ZodDefault<z.ZodEnum<{
76
+ applied: "applied";
77
+ conflicted: "conflicted";
78
+ obsolete: "obsolete";
79
+ upstreamed: "upstreamed";
80
+ }>>;
81
+ }, z.core.$strip>;
82
+ export type Rootstock = z.infer<typeof RootstockSchema>;
83
+ export type VariantRef = z.infer<typeof VariantRefSchema>;
84
+ export type Oracle = z.infer<typeof OracleSchema>;
85
+ export type ForkConfig = z.infer<typeof ForkSchema>;
86
+ export type Generator = z.infer<typeof GeneratorSchema>;
87
+ export type GraftFrontmatter = z.infer<typeof GraftFrontmatterSchema>;
88
+ /** Frontmatter as authored, before defaults are applied. */
89
+ export type GraftFrontmatterInput = z.input<typeof GraftFrontmatterSchema>;
90
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4BAA4B;AAC5B,eAAO,MAAM,cAAc,YAAI,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,CAAU,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,uEAAuE;AACvE,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EAA8B,CAAC;AAEjF,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AACvG,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,eAAO,MAAM,gBAAgB,QAAyB,CAAC;AAYvD,eAAO,MAAM,eAAe;;;iBAK1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;iBAK3B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;iBAUvB,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;iBA4BrB,CAAC;AAEH,eAAO,MAAM,eAAe;;;iBAe1B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAcjC,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AACpD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,4DAA4D;AAC5D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
package/dist/schema.js ADDED
@@ -0,0 +1,101 @@
1
+ import semver from 'semver';
2
+ import { z } from 'zod';
3
+ /** Lifecycle of a graft. */
4
+ export const GRAFT_STATUSES = ['applied', 'conflicted', 'upstreamed', 'obsolete'];
5
+ /** Statuses for which tooling still applies and verifies the graft. */
6
+ export const ACTIVE_STATUSES = ['applied', 'conflicted'];
7
+ /** Body sections every graft.md must contain, in recommended order. */
8
+ export const REQUIRED_SECTIONS = ['Why', 'Invariants', 'Where to look', 'Anchors', 'History'];
9
+ export const GRAFT_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
10
+ const semverVersion = z
11
+ .string()
12
+ .refine((v) => semver.valid(v) !== null, { message: 'must be a valid semver version (e.g. "4.2.0")' });
13
+ const semverRange = z
14
+ .string()
15
+ .refine((r) => semver.validRange(r, { includePrerelease: true }) !== null, {
16
+ message: 'must be a valid semver range (e.g. ">=4.0.0 <5.0.0")',
17
+ });
18
+ export const RootstockSchema = z.object({
19
+ /** npm package name of the upstream. */
20
+ package: z.string().min(1),
21
+ /** Last upstream version this graft was verified against by its oracle. */
22
+ verified: semverVersion,
23
+ });
24
+ export const VariantRefSchema = z.object({
25
+ /** Upstream version range this variant is valid for. */
26
+ range: semverRange,
27
+ /** Path to the variant module, relative to the graft directory. */
28
+ module: z.string().min(1),
29
+ });
30
+ export const OracleSchema = z.object({
31
+ /**
32
+ * Shell command that exits 0 iff the graft's invariants hold.
33
+ * Runs once per mode, with GRAFT_MODE set. `{mode}` in the command is substituted.
34
+ */
35
+ command: z.string().min(1),
36
+ /** Modes to run the oracle in (e.g. dev, build). */
37
+ modes: z.array(z.string().min(1)).min(1).default(['default']),
38
+ /** Optional timeout per mode, in seconds. */
39
+ timeout: z.number().int().positive().optional(),
40
+ });
41
+ export const ForkSchema = z.object({
42
+ /** Git URL (or path, relative to the project root) of the fork that carries the modification as commits. */
43
+ repo: z.string().min(1),
44
+ /** Git URL of the upstream source repository the fork tracks. */
45
+ upstream: z.string().min(1),
46
+ /** Upstream release tag for a version; `{version}` is substituted. */
47
+ tag: z
48
+ .string()
49
+ .min(1)
50
+ .default('v{version}')
51
+ .refine((t) => t.includes('{version}'), { message: 'must contain {version}' }),
52
+ /**
53
+ * Repo mode: an upstream branch to follow (e.g. `main`). `graft outdated` then offers its tip as
54
+ * `<version>+<commit>` instead of the newest release tag.
55
+ */
56
+ track: z.string().min(1).optional(),
57
+ /** Package directory inside the repositories (for monorepos). Default: the repo root. */
58
+ dir: z.string().min(1).optional(),
59
+ /**
60
+ * How the rebased fork is consumed. `package` (default): built, packed and installed into the
61
+ * project as a vendored tarball. `none`: the fork is the product itself (e.g. a translated docs
62
+ * site); nothing is installed and the oracle runs in the rebased checkout.
63
+ */
64
+ install: z.enum(['package', 'none']).default('package'),
65
+ /** Command run in the package directory of the rebased source before packing (e.g. `npm ci && npm run build`). */
66
+ build: z.string().min(1).optional(),
67
+ /** Optional timeout for the build, in seconds. */
68
+ timeout: z.number().int().positive().optional(),
69
+ });
70
+ export const GeneratorSchema = z.object({
71
+ /**
72
+ * Shell command that writes a new variant module for an upstream version, run from the
73
+ * project root. `{version}` is replaced with the target upstream version and `{out}` with
74
+ * the path to write the variant to (also exported as GRAFT_UPSTREAM_VERSION and
75
+ * GRAFT_VARIANT_OUT). `graft sync` runs it when no existing variant fits a new version.
76
+ */
77
+ command: z
78
+ .string()
79
+ .min(1)
80
+ .refine((c) => c.includes('{out}') || c.includes('GRAFT_VARIANT_OUT'), {
81
+ message: 'must write the variant to {out} (or $GRAFT_VARIANT_OUT)',
82
+ }),
83
+ /** Optional timeout, in seconds. */
84
+ timeout: z.number().int().positive().optional(),
85
+ });
86
+ export const GraftFrontmatterSchema = z.object({
87
+ id: z.string().regex(GRAFT_ID_PATTERN, 'must be lowercase kebab-case'),
88
+ rootstock: RootstockSchema,
89
+ /** Name of the mechanism adapter that applies this graft (e.g. patch). */
90
+ mechanism: z.string().min(1),
91
+ /** Upstream files the mechanism operates on, relative to the package root. */
92
+ targets: z.array(z.string().min(1)).optional(),
93
+ variants: z.array(VariantRefSchema).min(1),
94
+ oracle: OracleSchema,
95
+ /** Optional command that regenerates a variant for a new upstream version. */
96
+ generator: GeneratorSchema.optional(),
97
+ /** For `mechanism: fork`: where the modification lives as commits, and how to build it. */
98
+ fork: ForkSchema.optional(),
99
+ status: z.enum(GRAFT_STATUSES).default('applied'),
100
+ });
101
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { GraftFrontmatterInput } from './schema.js';
2
+ /** Render a starter graft.md with every required section and guidance for authors. */
3
+ export declare function renderGraftTemplate(frontmatter: GraftFrontmatterInput): string;
4
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,qBAAqB,GAAG,MAAM,CA8B9E"}
@@ -0,0 +1,34 @@
1
+ import { stringify } from 'yaml';
2
+ /** Render a starter graft.md with every required section and guidance for authors. */
3
+ export function renderGraftTemplate(frontmatter) {
4
+ const yaml = stringify(frontmatter, { lineWidth: 0 });
5
+ return `---
6
+ ${yaml}---
7
+
8
+ ## Why
9
+
10
+ <!-- What this modification enables, and why upstream does not (or will not) do it.
11
+ Link the upstream issue/PR if one exists. -->
12
+
13
+ ## Invariants
14
+
15
+ <!-- Observable behaviors that must hold. One bullet each. The oracle tests exactly these.
16
+ Example: "- Every request sent by the client carries an \`x-trace-id\` header." -->
17
+
18
+ ## Where to look
19
+
20
+ <!-- Describe the upstream area by *behavior*, not file path, so an agent can re-locate it
21
+ after a refactor. Example: "The function that builds outgoing request headers right
22
+ before the request is dispatched." -->
23
+
24
+ ## Anchors
25
+
26
+ <!-- For each anchor a variant matches on: what it is, and why it was chosen over
27
+ alternatives (prefer stable exported symbols and public strings over code shape). -->
28
+
29
+ ## History
30
+
31
+ - ${new Date().toISOString().slice(0, 10)}: Graft created against ${frontmatter.rootstock.package}@${frontmatter.rootstock.verified}.
32
+ `;
33
+ }
34
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAGjC,sFAAsF;AACtF,MAAM,UAAU,mBAAmB,CAAC,WAAkC;IACpE,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IACtD,OAAO;EACP,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;IAyBF,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,2BAA2B,WAAW,CAAC,SAAS,CAAC,OAAO,IAAI,WAAW,CAAC,SAAS,CAAC,QAAQ;CAClI,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "@graftsync/spec",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "The Graft spec: schema, parser and validator for graft intent files and manifests.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/brandonroberts/graft.git",
10
+ "directory": "packages/spec"
11
+ },
12
+ "homepage": "https://github.com/brandonroberts/graft#readme",
13
+ "bugs": "https://github.com/brandonroberts/graft/issues",
14
+ "keywords": [
15
+ "graft",
16
+ "patch",
17
+ "npm",
18
+ "upstream",
19
+ "spec",
20
+ "schema"
21
+ ],
22
+ "engines": {
23
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "dependencies": {
37
+ "semver": "^7.8.6",
38
+ "yaml": "^2.9.1",
39
+ "zod": "^4.6.7"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
6
44
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.