@popoverai/dotrequirements 0.27.0 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,7 +32,7 @@ pnpm add @popoverai/dotrequirements
32
32
  ### 1. Initialize a project
33
33
 
34
34
  ```bash
35
- npx dotreq init
35
+ npx @popoverai/dotrequirements@latest init
36
36
  ```
37
37
 
38
38
  This creates a `.requirements/` directory with example requirements and configures your project.
@@ -111,7 +111,7 @@ dotreq init --invite abc123xyz
111
111
  If a team admin shares an invite command with you, join the team and connect in one step:
112
112
 
113
113
  ```bash
114
- npx dotrequirements init --invite abc123xyz
114
+ npx @popoverai/dotrequirements@latest init --invite abc123xyz
115
115
  ```
116
116
 
117
117
  This shows the team name, prompts you to sign in, adds you to the team, and lets you select a project.
@@ -140,7 +140,7 @@ dotreq pull --share <token>
140
140
  If a team member shares a pull command with you, you can pull requirements without creating an account:
141
141
 
142
142
  ```bash
143
- npx @popoverai/dotrequirements pull --share drt_abc123...
143
+ npx @popoverai/dotrequirements@latest pull --share drt_abc123...
144
144
  ```
145
145
 
146
146
  This gives you read-only access to view requirements. To push changes or report coverage, run `dotreq link` afterward.
@@ -1,6 +1,7 @@
1
1
  import { getProjectContext } from "../requirements/cloud-ai.js";
2
2
  import { loadAllRequirements } from "../requirements/index.js";
3
- import { generateStyleGuide, readLocalStyleGuide, } from "../requirements/style-guide.js";
3
+ import { generateStyleGuide } from "../requirements/style-guide.js";
4
+ import { readLocalStyleGuide } from "../requirements/style-guide-file.js";
4
5
  import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
5
6
  const CONVEX_URL = "https://data.dotrequirements.io";
6
7
  export async function createRequirementDocumentCommand(filePath) {
@@ -262,7 +262,7 @@ async function mintShareArtifacts(client, teamId, projectId) {
262
262
  const share = (await client.mutation(api.projectSecrets.mutations.ensureShareToken,
263
263
  // biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
264
264
  { target: { type: "project", id: projectId } }));
265
- artifacts.sharePullCommand = `npx -y @popoverai/dotrequirements pull --share ${share.token}`;
265
+ artifacts.sharePullCommand = `npx -y @popoverai/dotrequirements@latest pull --share ${share.token}`;
266
266
  }
267
267
  catch {
268
268
  // LINK-13.3: connection already succeeded — omit rather than fail
@@ -2,7 +2,8 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { DEFAULT_API_BASE_URL, fetchStyleCheckFeedback, getProjectContext, } from "../requirements/cloud-ai.js";
4
4
  import { filterRequirementsByKeys, loadAllRequirements, } from "../requirements/index.js";
5
- import { generateStyleGuideBody, readLocalStyleGuide, } from "../requirements/style-guide.js";
5
+ import { generateStyleGuideBody } from "../requirements/style-guide.js";
6
+ import { readLocalStyleGuide } from "../requirements/style-guide-file.js";
6
7
  import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
7
8
  const CONVEX_URL = "https://data.dotrequirements.io";
8
9
  /**
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Filesystem access for the style guide — kept apart from the generator itself.
3
+ *
4
+ * `style-guide.ts` is imported by the web app to serve the same primer to chat
5
+ * agents (REMOTE-MCP-10), which have no repo to read. Everything in that module
6
+ * must therefore stay free of `node:fs`; the workspace lookups live here, where
7
+ * only the CLI reaches them. This mirrors the split the schema module already
8
+ * makes between its node and browser entry points.
9
+ */
10
+ /**
11
+ * Conventional location of a project's STYLE.md, relative to the workspace
12
+ * root. Exported so callers (init, push/pull, tests) can reference one place.
13
+ */
14
+ export declare const STYLE_MD_PATH = ".requirements/STYLE.md";
15
+ /**
16
+ * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
17
+ * contents on success, `null` if the file is absent. Empty / whitespace-only
18
+ * files are treated as absent so the bundled defaults still apply.
19
+ */
20
+ export declare function readLocalStyleGuide(workspaceRoot: string): string | null;
21
+ //# sourceMappingURL=style-guide-file.d.ts.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Filesystem access for the style guide — kept apart from the generator itself.
3
+ *
4
+ * `style-guide.ts` is imported by the web app to serve the same primer to chat
5
+ * agents (REMOTE-MCP-10), which have no repo to read. Everything in that module
6
+ * must therefore stay free of `node:fs`; the workspace lookups live here, where
7
+ * only the CLI reaches them. This mirrors the split the schema module already
8
+ * makes between its node and browser entry points.
9
+ */
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ /**
13
+ * Conventional location of a project's STYLE.md, relative to the workspace
14
+ * root. Exported so callers (init, push/pull, tests) can reference one place.
15
+ */
16
+ export const STYLE_MD_PATH = ".requirements/STYLE.md";
17
+ /**
18
+ * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
19
+ * contents on success, `null` if the file is absent. Empty / whitespace-only
20
+ * files are treated as absent so the bundled defaults still apply.
21
+ */
22
+ export function readLocalStyleGuide(workspaceRoot) {
23
+ const fullPath = join(workspaceRoot, STYLE_MD_PATH);
24
+ if (!existsSync(fullPath)) {
25
+ return null;
26
+ }
27
+ const contents = readFileSync(fullPath, "utf-8");
28
+ return contents.trim().length > 0 ? contents : null;
29
+ }
30
+ //# sourceMappingURL=style-guide-file.js.map
@@ -11,37 +11,35 @@
11
11
  *
12
12
  * If the project has a `.requirements/STYLE.md`, callers can pass its
13
13
  * contents via `localStyleGuide` to use that body in place of the bundled
14
- * defaults. See {@link readLocalStyleGuide} for the lookup helper.
14
+ * defaults. See `readLocalStyleGuide` in ./style-guide-file.ts (CLI-only —
15
+ * this module stays free of node:fs so the web can serve the same primer).
15
16
  */
16
17
  import type { FlattenedRequirement } from "./index.js";
17
18
  /**
18
- * Conventional location of a project's STYLE.md, relative to the workspace
19
- * root. Exported so callers (init, push/pull, tests) can reference one place.
19
+ * The only fields the guide reads off a requirement.
20
+ *
21
+ * Deliberately narrower than {@link FlattenedRequirement}, which is file-shaped
22
+ * (`sourceFile`, `documentTitle`). The web serves this same guide from cloud
23
+ * requirements, which have no file to name (REMOTE-MCP-10) — asking only for
24
+ * what's used lets both callers pass what they actually have instead of
25
+ * inventing filenames.
20
26
  */
21
- export declare const STYLE_MD_PATH = ".requirements/STYLE.md";
27
+ export type StyleGuideRequirement = Pick<FlattenedRequirement, "id" | "label" | "path">;
22
28
  export interface GenerateStyleGuideParams {
23
- /** Flattened view of the workspace's existing requirements, used to
24
- * surface label patterns and key-prefix patterns in the output. Pass
25
- * an empty array when no requirements exist yet. */
26
- requirements: FlattenedRequirement[];
27
- /** Project-owner-supplied custom guidance (currently sourced from the
28
- * cloud project's `requirementsStyleContext` field). Pass null/undefined
29
- * to omit. */
29
+ /** The existing requirements to discover label and key-prefix patterns from.
30
+ * Pass an empty array when none exist yet. */
31
+ requirements: StyleGuideRequirement[];
32
+ /** Project-owner-supplied custom guidance (sourced from the cloud project's
33
+ * `requirementsStyleContext` field). Pass null/undefined to omit. */
30
34
  customStyleGuidance?: string | null;
31
- /** Suggested target path for the new file. Used only in the preamble
32
- * and "Next Steps" section. Defaults to a generic example path. */
35
+ /** Suggested target path for the new file. Used only in the preamble and
36
+ * "Next Steps" section. Defaults to a generic example path. */
33
37
  filePath?: string;
34
- /** Project-local STYLE.md contents. When provided and non-empty, this
35
- * body replaces the bundled default; callers can fetch it via
36
- * {@link readLocalStyleGuide}. */
38
+ /** Project-local STYLE.md contents. When provided and non-empty, this body
39
+ * replaces the bundled default. CLI-only — see `readLocalStyleGuide` in
40
+ * ./style-guide-file.ts. */
37
41
  localStyleGuide?: string | null;
38
42
  }
39
- /**
40
- * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
41
- * contents on success, `null` if the file is absent. Empty / whitespace-only
42
- * files are treated as absent so the bundled defaults still apply.
43
- */
44
- export declare function readLocalStyleGuide(workspaceRoot: string): string | null;
45
43
  /**
46
44
  * Build the body of the bundled default style guide. This is the content
47
45
  * that lives inside the "# Requirements File Template" preamble — the
@@ -11,27 +11,47 @@
11
11
  *
12
12
  * If the project has a `.requirements/STYLE.md`, callers can pass its
13
13
  * contents via `localStyleGuide` to use that body in place of the bundled
14
- * defaults. See {@link readLocalStyleGuide} for the lookup helper.
14
+ * defaults. See `readLocalStyleGuide` in ./style-guide-file.ts (CLI-only —
15
+ * this module stays free of node:fs so the web can serve the same primer).
15
16
  */
16
- import { existsSync, readFileSync } from "node:fs";
17
- import { join } from "node:path";
17
+ import { ROOT_LABEL_MARKER } from "../schema/parser-core.js";
18
18
  /**
19
- * Conventional location of a project's STYLE.md, relative to the workspace
20
- * root. Exported so callers (init, push/pull, tests) can reference one place.
19
+ * How many discovered labels or key prefixes the guide will show.
20
+ *
21
+ * These lists exist under "match the existing pattern", and past ten that
22
+ * instruction stops being actionable — a reader can't match a hundred and forty
23
+ * things. Worse, an uncapped list presents whatever sprawl a codebase has
24
+ * accumulated as the house style, so each new author reproduces it and the list
25
+ * grows: the mechanism meant to hold conventions steady is what compounds their
26
+ * drift. Ten is a sample, and the trailing count says so rather than letting the
27
+ * truncation read as the whole set.
21
28
  */
22
- export const STYLE_MD_PATH = ".requirements/STYLE.md";
29
+ const SAMPLE_LIMIT = 10;
23
30
  /**
24
- * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
25
- * contents on success, `null` if the file is absent. Empty / whitespace-only
26
- * files are treated as absent so the bundled defaults still apply.
31
+ * The most-used values first, not the alphabetically-first.
32
+ *
33
+ * Sorting by name and taking the head is a trap: on this codebase it yields ten
34
+ * prefixes all beginning with "A", which under "match the existing pattern"
35
+ * teaches a convention nobody has. Frequency is what the phrase actually means —
36
+ * the prefixes carrying the most requirements are the house style. Ties break
37
+ * alphabetically so the sample is stable across runs.
27
38
  */
28
- export function readLocalStyleGuide(workspaceRoot) {
29
- const fullPath = join(workspaceRoot, STYLE_MD_PATH);
30
- if (!existsSync(fullPath)) {
31
- return null;
32
- }
33
- const contents = readFileSync(fullPath, "utf-8");
34
- return contents.trim().length > 0 ? contents : null;
39
+ function sample(counts) {
40
+ const ranked = Array.from(counts.entries())
41
+ .sort(([aName, aCount], [bName, bCount]) => bCount === aCount ? aName.localeCompare(bName) : bCount - aCount)
42
+ .map(([name]) => name);
43
+ return {
44
+ list: ranked
45
+ .slice(0, SAMPLE_LIMIT)
46
+ .map((v) => `"${v}"`)
47
+ .join(", "),
48
+ more: ranked.length > SAMPLE_LIMIT
49
+ ? ` (and ${ranked.length - SAMPLE_LIMIT} more)`
50
+ : "",
51
+ };
52
+ }
53
+ function tally(counts, key) {
54
+ counts.set(key, (counts.get(key) ?? 0) + 1);
35
55
  }
36
56
  /**
37
57
  * Build the body of the bundled default style guide. This is the content
@@ -46,43 +66,45 @@ export function readLocalStyleGuide(workspaceRoot) {
46
66
  */
47
67
  export function generateStyleGuideBody(params) {
48
68
  const { requirements, customStyleGuidance } = params;
49
- const labels = new Set();
50
- const keyPrefixes = new Set();
69
+ const labels = new Map();
70
+ const keyPrefixes = new Map();
51
71
  for (const req of requirements) {
52
- if (req.label?.trim()) {
53
- labels.add(req.label);
72
+ // The root marker is on every root in every workspace and chosen by nobody,
73
+ // so counting it would hand back a storage detail as house style. Worst for
74
+ // a project that took this guide's own advice to leave criteria unlabelled:
75
+ // it would be the only label found, and would suppress the "default to
76
+ // unlabeled" branch below in favour of matching a label nobody types.
77
+ if (req.label?.trim() && req.label !== ROOT_LABEL_MARKER) {
78
+ tally(labels, req.label);
54
79
  }
55
80
  if (req.path.length === 0 && req.id) {
56
81
  const lastDashIndex = req.id.lastIndexOf("-");
57
82
  if (lastDashIndex > 0) {
58
- keyPrefixes.add(req.id.substring(0, lastDashIndex));
83
+ tally(keyPrefixes, req.id.substring(0, lastDashIndex));
59
84
  }
60
85
  }
61
86
  }
62
- const discoveredLabels = Array.from(labels).sort();
63
- const discoveredPrefixes = Array.from(keyPrefixes).sort();
64
87
  let labelGuidance;
65
- if (discoveredLabels.length > 0) {
66
- const labelList = discoveredLabels
67
- .slice(0, 10)
68
- .map((l) => `"${l}"`)
69
- .join(", ");
70
- const more = discoveredLabels.length > 10
71
- ? ` (and ${discoveredLabels.length - 10} more)`
72
- : "";
88
+ if (labels.size > 0) {
89
+ const { list: labelList, more } = sample(labels);
73
90
  labelGuidance = `**Existing labels in this codebase:** ${labelList}${more}
74
91
 
75
92
  **Use these existing labels** to maintain consistency. If you're unsure which labels to use for a new requirement, ask the user.`;
76
93
  }
77
94
  else {
78
- labelGuidance = `**No existing requirements found in this codebase.**
95
+ // Says *labels*, not requirements. Reaching here means nobody has labelled
96
+ // a criterion — which is what a project following the advice below looks
97
+ // like, not an empty one. The key-prefix branch shares this sentence and is
98
+ // right to: there, an empty set really does mean no requirements. Here it
99
+ // would contradict the prefix list printed two headings above.
100
+ labelGuidance = `**No labelled criteria found in this codebase.**
79
101
 
80
102
  **Default to unlabeled requirements** (\`0. → content\`). If the user wants labels, ask them which format they prefer. Do not choose an opinionated framework like Given/When/Then without explicit user consent.`;
81
103
  }
82
104
  let keyGuidance;
83
- if (discoveredPrefixes.length > 0) {
84
- const prefixList = discoveredPrefixes.map((p) => `"${p}"`).join(", ");
85
- keyGuidance = `**Existing requirement key prefixes in this codebase:** ${prefixList}
105
+ if (keyPrefixes.size > 0) {
106
+ const { list: prefixList, more } = sample(keyPrefixes);
107
+ keyGuidance = `**Existing requirement key prefixes in this codebase:** ${prefixList}${more}
86
108
 
87
109
  **Match the existing pattern** when creating new requirement keys. Use the same domain prefixes and sequential numbering style.`;
88
110
  }
@@ -6,7 +6,7 @@
6
6
  export { buildRequirementMarkdown, buildRequirementsMarkdown, DEFAULT_DELIMITER, } from "./builder.js";
7
7
  export type { ConvexRequirement } from "./conversions.js";
8
8
  export { buildMetadata, constructKey, convexToRequirements, extractRequirementKeys, groupByRoot, parseKey, requirementsToConvex, } from "./conversions.js";
9
- export { DELIMITER_PATTERN, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine, } from "./parser-core.js";
9
+ export { DELIMITER_PATTERN, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine, ROOT_LABEL_MARKER, } from "./parser-core.js";
10
10
  export type { Metadata, ParsedCriterion, RequirementKey, RequirementNode, RequirementPrefix, RequirementsFile, } from "./schemas.js";
11
11
  export { buildRequirementKey, MetadataSchema, normalizePrefix, ParsedCriterionSchema, parseRequirementKey, REQUIREMENT_KEY_PATTERN, REQUIREMENT_PREFIX_PATTERN, RequirementKeySchema, RequirementNodeSchema, RequirementPrefixSchema, RequirementsFileSchema, ValidationError, validateKey, validateMetadata, validatePrefix, validateRequirementNode, validateRequirementsFile, } from "./schemas.js";
12
12
  export type { BuildScenarioOptions, Scenario, ScenarioAssertionSource, ScenarioStep, } from "./scenario.js";
@@ -8,7 +8,10 @@ export { buildRequirementMarkdown, buildRequirementsMarkdown, DEFAULT_DELIMITER,
8
8
  export { buildMetadata, constructKey, convexToRequirements, extractRequirementKeys, groupByRoot, parseKey, requirementsToConvex, } from "./conversions.js";
9
9
  // Parser core (pure TypeScript - browser-safe, no fs dependency)
10
10
  // These functions parse markdown strings directly without file I/O
11
- export { DELIMITER_PATTERN, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine, } from "./parser-core.js";
11
+ export { DELIMITER_PATTERN, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine,
12
+ // The web reaches parser-core only through this entry point, so without this
13
+ // its readers have no way to import the marker and must hard-code it.
14
+ ROOT_LABEL_MARKER, } from "./parser-core.js";
12
15
  // Schemas and types (uses zod - browser-safe)
13
16
  export { buildRequirementKey,
14
17
  // Zod schemas
@@ -7,7 +7,7 @@
7
7
  export { buildRequirementMarkdown, buildRequirementsFile, buildRequirementsMarkdown, } from "./builder.js";
8
8
  export type { ConvexRequirement } from "./conversions.js";
9
9
  export { buildMetadata, constructKey, convexToRequirements, extractRequirementKeys, groupByRoot, parseKey, requirementsToConvex, } from "./conversions.js";
10
- export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementsFile, parseRequirementsFromFile, } from "./parser.js";
10
+ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementsFile, parseRequirementsFromFile, parseRootLine, } from "./parser.js";
11
11
  export { checkPathAmbiguity, findChildrenByLabel, getAllLabelPaths, parsePathSegment, parseRequirementPath, resolvePathSegment, resolveRequirementPath, resolveToNumericPath, } from "./resolver.js";
12
12
  export type { BuildScenarioOptions, Scenario, ScenarioAssertionSource, ScenarioStep, } from "./scenario.js";
13
13
  export { buildScenarioFromRequirements, requirementTreeToScenario, } from "./scenario.js";
@@ -8,7 +8,7 @@
8
8
  export { buildRequirementMarkdown, buildRequirementsFile, buildRequirementsMarkdown, } from "./builder.js";
9
9
  export { buildMetadata, constructKey, convexToRequirements, extractRequirementKeys, groupByRoot, parseKey, requirementsToConvex, } from "./conversions.js";
10
10
  // Parsing
11
- export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementsFile, parseRequirementsFromFile, } from "./parser.js";
11
+ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementsFile, parseRequirementsFromFile, parseRootLine, } from "./parser.js";
12
12
  // Path resolution
13
13
  export { checkPathAmbiguity, findChildrenByLabel, getAllLabelPaths, parsePathSegment, parseRequirementPath, resolvePathSegment, resolveRequirementPath, resolveToNumericPath, } from "./resolver.js";
14
14
  // Scenario building (for browser-automation / runScenario)
@@ -5,6 +5,21 @@
5
5
  * making it safe to import in Convex runtime or browser environments.
6
6
  */
7
7
  import { type ParsedCriterion, type RequirementNode } from "./schemas.js";
8
+ /**
9
+ * The label stamped on every requirement's root, for indexing and querying.
10
+ *
11
+ * An internal marker, not a convention: no author writes it, the markdown
12
+ * format has no syntax for it, and consumers that show labels to a human or an
13
+ * agent must exclude it — the harness's output filters, Convex's label queries,
14
+ * and the style guide's discovered-label list all do.
15
+ *
16
+ * Declared here because this is where parsing stamps it, but be warned: most of
17
+ * those readers still hard-code the literal, and `web/lib/markdown-conversion`
18
+ * stamps it independently for the BlockNote path. So renaming this does NOT yet
19
+ * move everyone with it — you would have to grep. Each reader migrated here is
20
+ * one that a rename can no longer silently strand.
21
+ */
22
+ export declare const ROOT_LABEL_MARKER = "requirementHeader";
8
23
  /**
9
24
  * Default delimiter for requirements.
10
25
  * Can be overridden for organization-specific preferences.
@@ -5,6 +5,21 @@
5
5
  * making it safe to import in Convex runtime or browser environments.
6
6
  */
7
7
  import { ValidationError, validateKey, } from "./schemas.js";
8
+ /**
9
+ * The label stamped on every requirement's root, for indexing and querying.
10
+ *
11
+ * An internal marker, not a convention: no author writes it, the markdown
12
+ * format has no syntax for it, and consumers that show labels to a human or an
13
+ * agent must exclude it — the harness's output filters, Convex's label queries,
14
+ * and the style guide's discovered-label list all do.
15
+ *
16
+ * Declared here because this is where parsing stamps it, but be warned: most of
17
+ * those readers still hard-code the literal, and `web/lib/markdown-conversion`
18
+ * stamps it independently for the BlockNote path. So renaming this does NOT yet
19
+ * move everyone with it — you would have to grep. Each reader migrated here is
20
+ * one that a rename can no longer silently strand.
21
+ */
22
+ export const ROOT_LABEL_MARKER = "requirementHeader";
8
23
  /**
9
24
  * Default delimiter for requirements.
10
25
  * Can be overridden for organization-specific preferences.
@@ -103,9 +118,9 @@ export function parseRequirementBlock(key, blockContent) {
103
118
  }
104
119
  const root = {
105
120
  id: key,
106
- // Root requirements always use "requirementHeader" label for indexing/querying
107
- // The markdown format doesn't preserve root labels, but we standardize on this value
108
- label: "requirementHeader",
121
+ // Root requirements always use this label for indexing/querying. The
122
+ // markdown format doesn't preserve root labels, so we standardize on it.
123
+ label: ROOT_LABEL_MARKER,
109
124
  content: rootParsed.content,
110
125
  children: [],
111
126
  };
@@ -8,7 +8,7 @@
8
8
  * requirements file means (SYNC-FORMAT-2.3).
9
9
  */
10
10
  import { type RequirementNode, type RequirementsFile } from "./schemas.js";
11
- export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, } from "./parser-core.js";
11
+ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRootLine, } from "./parser-core.js";
12
12
  /**
13
13
  * Parse a complete requirements Markdown file.
14
14
  */
@@ -13,7 +13,7 @@ import { parseRequirementBlocksFromMarkdown, splitFrontmatter, } from "./parser-
13
13
  import { ValidationError, validateMetadata, } from "./schemas.js";
14
14
  // Block/criterion parsing and tree utilities live in parser-core; re-exported
15
15
  // here so the schema module's public surface is unchanged.
16
- export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, } from "./parser-core.js";
16
+ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRootLine, } from "./parser-core.js";
17
17
  /**
18
18
  * Extract YAML frontmatter from Markdown content.
19
19
  * Returns { frontmatter, body } where frontmatter is the parsed YAML object.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popoverai/dotrequirements",
3
- "version": "0.27.0",
3
+ "version": "0.27.2",
4
4
  "description": "Requirements tracking CLI and test harness",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,8 @@
11
11
  ".": "./dist/cli.js",
12
12
  "./test": "./dist/harness/index.js",
13
13
  "./schema": "./dist/schema/index.js",
14
- "./schema/browser": "./dist/schema/browser.js"
14
+ "./schema/browser": "./dist/schema/browser.js",
15
+ "./style-guide": "./dist/requirements/style-guide.js"
15
16
  },
16
17
  "publishConfig": {
17
18
  "access": "public"