@popoverai/dotrequirements 0.26.2 → 0.27.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.
Files changed (56) hide show
  1. package/README.md +13 -69
  2. package/dist/cli.js +19 -7
  3. package/dist/commands/ai-setup.d.ts +8 -2
  4. package/dist/commands/ai-setup.js +153 -347
  5. package/dist/commands/create-requirement-document.js +2 -1
  6. package/dist/commands/init.js +1 -1
  7. package/dist/commands/mcp.d.ts +8 -2
  8. package/dist/commands/mcp.js +17 -6
  9. package/dist/commands/review-test.d.ts +5 -1
  10. package/dist/commands/review-test.js +101 -7
  11. package/dist/commands/style-check.d.ts +1 -0
  12. package/dist/commands/style-check.js +138 -13
  13. package/dist/convex.d.ts +1 -3
  14. package/dist/convex.js +3 -3
  15. package/dist/requirements/cloud-ai.d.ts +21 -8
  16. package/dist/requirements/cloud-ai.js +10 -8
  17. package/dist/requirements/style-guide-file.d.ts +21 -0
  18. package/dist/requirements/style-guide-file.js +30 -0
  19. package/dist/requirements/style-guide.d.ts +20 -22
  20. package/dist/requirements/style-guide.js +57 -35
  21. package/dist/schema/browser.d.ts +1 -1
  22. package/dist/schema/browser.js +4 -1
  23. package/dist/schema/parser-core.d.ts +28 -0
  24. package/dist/schema/parser-core.js +51 -12
  25. package/dist/templates/context-file-section.md +25 -22
  26. package/dist/utils/context-file.d.ts +7 -3
  27. package/dist/utils/context-file.js +10 -7
  28. package/dist/utils/project-settings.d.ts +1 -0
  29. package/dist/utils/project-settings.js +22 -0
  30. package/package.json +3 -4
  31. package/dist/mcp/convexClient.d.ts +0 -19
  32. package/dist/mcp/convexClient.js +0 -24
  33. package/dist/mcp/handlers/authoring.d.ts +0 -41
  34. package/dist/mcp/handlers/authoring.js +0 -113
  35. package/dist/mcp/handlers/debug.d.ts +0 -16
  36. package/dist/mcp/handlers/debug.js +0 -37
  37. package/dist/mcp/handlers/get.d.ts +0 -24
  38. package/dist/mcp/handlers/get.js +0 -69
  39. package/dist/mcp/handlers/index.d.ts +0 -28
  40. package/dist/mcp/handlers/index.js +0 -19
  41. package/dist/mcp/handlers/list.d.ts +0 -7
  42. package/dist/mcp/handlers/list.js +0 -43
  43. package/dist/mcp/handlers/push.d.ts +0 -26
  44. package/dist/mcp/handlers/push.js +0 -232
  45. package/dist/mcp/handlers/report.d.ts +0 -16
  46. package/dist/mcp/handlers/report.js +0 -134
  47. package/dist/mcp/handlers/review.d.ts +0 -52
  48. package/dist/mcp/handlers/review.js +0 -243
  49. package/dist/mcp/handlers/search.d.ts +0 -30
  50. package/dist/mcp/handlers/search.js +0 -58
  51. package/dist/mcp/handlers/test-mapping.d.ts +0 -39
  52. package/dist/mcp/handlers/test-mapping.js +0 -168
  53. package/dist/mcp/handlers/types.d.ts +0 -89
  54. package/dist/mcp/handlers/types.js +0 -52
  55. package/dist/mcp/index.d.ts +0 -45
  56. package/dist/mcp/index.js +0 -638
@@ -4,14 +4,32 @@ import { DEFAULT_API_BASE_URL, fetchReviewTestFeedback, } from "../requirements/
4
4
  import { findRequirementsInFile } from "../requirements/grep.js";
5
5
  import { formatRequirementTree, getRequirementTree, loadAllRequirements, } from "../requirements/index.js";
6
6
  import { getProjectCredentials } from "../utils/project-settings.js";
7
- export async function reviewTestCommand(testFilePath) {
7
+ /**
8
+ * Judgment instructions for local-mode test review. Mirrors the hosted
9
+ * review's semantic framing: test anatomy (setup, actions, assertions)
10
+ * checked against requirement anatomy (preconditions, triggers, outcomes).
11
+ */
12
+ const REVIEW_INSTRUCTIONS = `You are the reviewer. Judge whether the test file below validates what its referenced requirements specify, then report findings under the headings MUST FIX / SHOULD FIX / COULD IMPROVE (semantic gaps are MUST FIX; categorize other findings by their own severity). Focus on gaps and give actionable suggestions.
13
+
14
+ For each requirement reference, check:
15
+ - **Setup vs preconditions** — does the test establish the state the requirement describes (e.g. "a registered user" backed by actual data, not mocked away)?
16
+ - **Actions vs triggers** — does the test exercise the specified behavior with the specified inputs?
17
+ - **Assertions vs outcomes** — does the test verify the specified outcomes (no tautologies), and does it cover every distinct outcome the requirement lists?
18
+ - Note over-testing (validating behavior the requirement doesn't specify) without treating it as an error.
19
+ - When a test's level doesn't fit its requirement (e.g. a unit test referencing an end-to-end requirement), suggest scoping the reference or a different test level — with specific guidance.`;
20
+ export async function reviewTestCommand(testFilePath, options = {}) {
8
21
  const workspaceRoot = process.cwd();
9
22
  const fullPath = resolve(workspaceRoot, testFilePath);
10
- // CLI-REVIEW-1.5: missing file → error + non-zero exit
23
+ // CLI-REVIEW-1.5: --source accepts only local|cloud
24
+ const source = options.source ?? "local";
25
+ if (source !== "local" && source !== "cloud") {
26
+ throw new Error(`Invalid --source value: ${options.source}. Expected "local" or "cloud".`);
27
+ }
28
+ // CLI-REVIEW-1.3: missing file → error + non-zero exit
11
29
  if (!existsSync(fullPath)) {
12
30
  throw new Error(`File not found: ${testFilePath}`);
13
31
  }
14
- // CLI-REVIEW-1.6: must be a test file
32
+ // CLI-REVIEW-1.4: must be a test file
15
33
  if (!/\.(test|spec)\.(js|jsx|ts|tsx)$/.test(testFilePath)) {
16
34
  throw new Error(`File must be a test file: *.{test,spec}.{js,jsx,ts,tsx} — got ${testFilePath}`);
17
35
  }
@@ -30,6 +48,9 @@ export async function reviewTestCommand(testFilePath) {
30
48
  // resolve instead of being dropped.
31
49
  const { flattened } = await loadAllRequirements(workspaceRoot);
32
50
  const rootToTestedIds = new Map();
51
+ // CLI-REVIEW-1.2: references whose root resolves to no workspace
52
+ // requirement are reported as missing (in every mode)
53
+ const missingIds = [];
33
54
  for (const reqId of requirementIds) {
34
55
  const dotIndex = reqId.indexOf(".");
35
56
  const refRootId = dotIndex > 0 ? reqId.substring(0, dotIndex) : reqId;
@@ -40,7 +61,11 @@ export async function reviewTestCommand(testFilePath) {
40
61
  }
41
62
  rootToTestedIds.get(req.rootId).add(reqId);
42
63
  }
64
+ else {
65
+ missingIds.push(reqId);
66
+ }
43
67
  }
68
+ missingIds.sort();
44
69
  const requirements = [];
45
70
  for (const [rootId, testedIds] of rootToTestedIds) {
46
71
  const tree = getRequirementTree(flattened, rootId);
@@ -50,7 +75,64 @@ export async function reviewTestCommand(testFilePath) {
50
75
  testedIds: Array.from(testedIds).sort(),
51
76
  });
52
77
  }
53
- // CLI-REVIEW-1.6: cloud credentials required
78
+ if (source === "cloud") {
79
+ await runCloudReview({
80
+ workspaceRoot,
81
+ testFilePath,
82
+ testFileContents,
83
+ requirements,
84
+ missingIds,
85
+ });
86
+ return;
87
+ }
88
+ emitReviewMaterials({
89
+ testFilePath,
90
+ testFileContents,
91
+ requirements,
92
+ missingIds,
93
+ });
94
+ }
95
+ /**
96
+ * CLI-REVIEW-2: local (default) mode — emit judgment-ready review materials
97
+ * for the calling agent (typically a dispatched review subagent). No cloud
98
+ * credentials required.
99
+ */
100
+ function emitReviewMaterials(params) {
101
+ const { testFilePath, testFileContents, requirements, missingIds } = params;
102
+ console.log(`# Test Review Materials for ${testFilePath}\n`);
103
+ console.log(`## Judgment instructions\n`);
104
+ console.log(REVIEW_INSTRUCTIONS);
105
+ // CLI-REVIEW-2.0: resolved requirement trees bundled with the test contents
106
+ console.log(`\n## Referenced requirements\n`);
107
+ if (requirements.length === 0) {
108
+ console.log("(none resolved — the test file references no workspace requirements)");
109
+ }
110
+ else {
111
+ for (const req of requirements) {
112
+ console.log(`### ${req.id} (tested: ${req.testedIds.join(", ")})\n`);
113
+ console.log(req.content);
114
+ console.log("");
115
+ }
116
+ }
117
+ // CLI-REVIEW-1.2: missing references reported in the output
118
+ if (missingIds.length > 0) {
119
+ console.log(`## Missing requirement references\n`);
120
+ console.log(`These references resolve to no requirement in this workspace — flag them in your findings:`);
121
+ for (const id of missingIds) {
122
+ console.log(`- ${id}`);
123
+ }
124
+ console.log("");
125
+ }
126
+ console.log(`## Test file under review (${testFilePath})\n`);
127
+ console.log(testFileContents);
128
+ }
129
+ /**
130
+ * CLI-REVIEW-3: --source cloud — send the bundle to the hosted review
131
+ * endpoint and print the returned feedback. Requires cloud credentials.
132
+ */
133
+ async function runCloudReview(params) {
134
+ const { workspaceRoot, testFilePath, testFileContents, requirements, missingIds, } = params;
135
+ // CLI-REVIEW-3.2: cloud credentials required
54
136
  let projectId;
55
137
  let projectSecret;
56
138
  try {
@@ -62,22 +144,34 @@ export async function reviewTestCommand(testFilePath) {
62
144
  throw new Error(`Test review requires cloud credentials. Run \`dotrequirements link\` to connect this project to the cloud.\n` +
63
145
  `(${error instanceof Error ? error.message : String(error)})`);
64
146
  }
65
- // CLI-REVIEW-1.2 / .3 / .4 / .7 / .8: dispatch to the hosted endpoint
147
+ // CLI-REVIEW-3.0 / .1 / .3: dispatch to the hosted endpoint
66
148
  const apiBaseUrl = process.env.DOTREQUIREMENTS_API_URL ?? DEFAULT_API_BASE_URL;
67
149
  let feedback;
150
+ let quotaWarning;
68
151
  try {
69
- feedback = await fetchReviewTestFeedback({
152
+ ({ feedback, quotaWarning } = await fetchReviewTestFeedback({
70
153
  apiBaseUrl,
71
154
  projectId,
72
155
  projectSecret,
73
156
  testFileContents,
74
157
  requirements,
75
- });
158
+ }));
76
159
  }
77
160
  catch (error) {
78
161
  throw new Error(`Test review failed: ${error instanceof Error ? error.message : String(error)}`);
79
162
  }
80
163
  console.log(`Test Review Results for ${testFilePath}\n`);
81
164
  console.log(feedback);
165
+ // LIMITS-5.2 / 5.2.0: the 80–99% quota warning prints with the result
166
+ if (quotaWarning) {
167
+ console.log(`\n⚠ ${quotaWarning}`);
168
+ }
169
+ // CLI-REVIEW-1.2: missing references reported in every mode
170
+ if (missingIds.length > 0) {
171
+ console.log(`\nMissing requirement references (not in this workspace):`);
172
+ for (const id of missingIds) {
173
+ console.log(`- ${id}`);
174
+ }
175
+ }
82
176
  }
83
177
  //# sourceMappingURL=review-test.js.map
@@ -1,6 +1,7 @@
1
1
  interface StyleCheckOptions {
2
2
  keys?: string[];
3
3
  model?: string;
4
+ source?: string;
4
5
  }
5
6
  export declare function styleCheckCommand(filePath: string, options: StyleCheckOptions): Promise<void>;
6
7
  export {};
@@ -1,16 +1,45 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { DEFAULT_API_BASE_URL, fetchStyleCheckFeedback, } from "../requirements/cloud-ai.js";
4
- import { filterRequirementsByKeys } from "../requirements/index.js";
5
- import { getProjectCredentials } from "../utils/project-settings.js";
3
+ import { DEFAULT_API_BASE_URL, fetchStyleCheckFeedback, getProjectContext, } from "../requirements/cloud-ai.js";
4
+ import { filterRequirementsByKeys, loadAllRequirements, } from "../requirements/index.js";
5
+ import { generateStyleGuideBody } from "../requirements/style-guide.js";
6
+ import { readLocalStyleGuide } from "../requirements/style-guide-file.js";
7
+ import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
8
+ const CONVEX_URL = "https://data.dotrequirements.io";
9
+ /**
10
+ * Judgment instructions for test-file style review in local mode. Mirrors the
11
+ * hosted test-style rubric (PROMPT-STYLE-TESTS-*): requirement() usage,
12
+ * comment discipline, structure, coverage-comment red flags, and semantic
13
+ * alignment between test anatomy and requirement anatomy.
14
+ */
15
+ const TEST_STYLE_INSTRUCTIONS = `You are the reviewer. Judge the test file below against these test-style conventions, then report findings under the headings MUST FIX / SHOULD FIX / COULD IMPROVE (categorize each finding by its own severity; semantic gaps are MUST FIX). Focus on gaps and give actionable suggestions.
16
+
17
+ - \`requirement()\` is used AS the description parameter of \`test()\`, \`describe()\`, or \`it()\` — flag requirement() calls placed in test bodies, and plain-string descriptions that should reference a requirement.
18
+ - Comments describe what the test implementation does — flag comments that copy requirement text verbatim (the requirement() reference already carries that meaning). Tests need no comments at all.
19
+ - Structured requirements (Given/When/Then trees) are tested with nested describe/it blocks; simple requirements need no extra nesting.
20
+ - Flag comments that document requirement coverage (e.g. "Requirements coverage: AUTH-9.0 ✓") — coverage lives in the harness, not comments; a second source of truth drifts.
21
+ - Test setup should establish the preconditions the referenced requirement describes; actions should exercise the specified behavior with the specified inputs; assertions should verify the specified outcomes (not tautologies like expect(true).toBe(true)). Flag requirements whose distinct outcomes are only partially validated; note over-testing without treating it as an error.
22
+ - When a test's level doesn't fit its requirement (e.g. a unit test referencing an end-to-end requirement), suggest scoping the reference or a different test level — with specific guidance, not generic observations.
23
+
24
+ The referenced requirement trees are not bundled here; if you need one, read it from \`.requirements/\` or run \`dotreq get <KEY>\`. For a full semantic review of tests against their requirements, \`dotreq review-test <file>\` is the dedicated verb.`;
25
+ const REQUIREMENTS_STYLE_INSTRUCTIONS = `You are the reviewer. Judge the requirements below against the style guide above, then report findings under the headings MUST FIX / SHOULD FIX / COULD IMPROVE (categorize each finding by its own severity). Focus on gaps and give actionable suggestions rather than just listing problems.`;
6
26
  export async function styleCheckCommand(filePath, options) {
7
27
  const workspaceRoot = process.cwd();
8
28
  const fullPath = resolve(workspaceRoot, filePath);
9
- // CLI-STYLE-1.5: missing file → error + non-zero exit
29
+ // CLI-STYLE-1.6: --source accepts only local|cloud
30
+ const source = options.source ?? "local";
31
+ if (source !== "local" && source !== "cloud") {
32
+ throw new Error(`Invalid --source value: ${options.source}. Expected "local" or "cloud".`);
33
+ }
34
+ // CLI-STYLE-1.7: --model configures the hosted review only
35
+ if (options.model && source !== "cloud") {
36
+ throw new Error(`--model applies to cloud review only. Add --source cloud to choose a review model.`);
37
+ }
38
+ // CLI-STYLE-1.4: missing file → error + non-zero exit
10
39
  if (!existsSync(fullPath)) {
11
40
  throw new Error(`File not found: ${filePath}`);
12
41
  }
13
- // CLI-STYLE-1.0 / .1 / .6: file-type detection
42
+ // CLI-STYLE-1.0 / .1 / .5: file-type detection
14
43
  const isRequirementsFile = filePath.endsWith(".requirements.md");
15
44
  const isTestFile = /\.(test|spec)\.(js|jsx|ts|tsx)$/.test(filePath);
16
45
  if (!isRequirementsFile && !isTestFile) {
@@ -35,7 +64,100 @@ export async function styleCheckCommand(filePath, options) {
35
64
  scopeNote = `\nNote: Some specified keys were not found in the file: ${missingKeys.join(", ")}`;
36
65
  }
37
66
  }
38
- // CLI-STYLE-1.7: credentials are required
67
+ const scopeLabel = options.keys && options.keys.length > 0
68
+ ? ` (${options.keys.join(", ")})`
69
+ : "";
70
+ if (source === "cloud") {
71
+ await runCloudStyleCheck({
72
+ workspaceRoot,
73
+ filePath,
74
+ fileContentsToCheck,
75
+ fileType,
76
+ model: options.model,
77
+ scopeLabel,
78
+ scopeNote,
79
+ });
80
+ return;
81
+ }
82
+ await emitStyleMaterials({
83
+ workspaceRoot,
84
+ filePath,
85
+ fileContentsToCheck,
86
+ fileType,
87
+ scopeLabel,
88
+ scopeNote,
89
+ });
90
+ }
91
+ /**
92
+ * CLI-STYLE-2: local (default) mode — emit judgment-ready review materials
93
+ * for the calling agent (typically a dispatched review subagent). No cloud
94
+ * credentials required.
95
+ */
96
+ async function emitStyleMaterials(params) {
97
+ const { workspaceRoot, filePath, fileContentsToCheck, fileType, scopeLabel, scopeNote, } = params;
98
+ console.log(`# Style Review Materials for ${filePath}${scopeLabel}\n`);
99
+ if (fileType === "requirements") {
100
+ // CLI-STYLE-2.0: compose the style guide the same way
101
+ // create-requirement-document does.
102
+ // CLI-STYLE-2.0.0: a project STYLE.md, when present, IS the guide.
103
+ const localStyleGuide = readLocalStyleGuide(workspaceRoot);
104
+ let guide;
105
+ if (localStyleGuide?.trim()) {
106
+ guide = localStyleGuide;
107
+ }
108
+ else {
109
+ // CLI-STYLE-2.0.1: otherwise generate from bundled defaults, workspace
110
+ // patterns, and cloud custom guidance when linked.
111
+ let requirements = [];
112
+ try {
113
+ const result = await loadAllRequirements(workspaceRoot);
114
+ requirements = result.flattened;
115
+ }
116
+ catch {
117
+ // No requirements in the workspace yet — defaults still apply
118
+ }
119
+ // CLI-STYLE-2.3 / .4: credentials are optional; cloud custom guidance is
120
+ // silently omitted when unlinked or unreachable.
121
+ let customStyleGuidance = null;
122
+ const projectRoot = findProjectRoot(workspaceRoot);
123
+ if (projectRoot) {
124
+ try {
125
+ const { projectId, projectSecret } = getProjectCredentials(workspaceRoot);
126
+ const contextData = await getProjectContext(projectId, projectSecret, CONVEX_URL);
127
+ customStyleGuidance = contextData?.requirementsStyleContext ?? null;
128
+ }
129
+ catch {
130
+ // Unlinked or cloud unavailable — fall through with defaults
131
+ }
132
+ }
133
+ guide = generateStyleGuideBody({ requirements, customStyleGuidance });
134
+ }
135
+ console.log(`## Style guide\n`);
136
+ console.log(guide);
137
+ console.log(`\n## Judgment instructions\n`);
138
+ console.log(REQUIREMENTS_STYLE_INSTRUCTIONS);
139
+ }
140
+ else {
141
+ console.log(`## Judgment instructions\n`);
142
+ console.log(TEST_STYLE_INSTRUCTIONS);
143
+ }
144
+ // CLI-STYLE-2.1: the content under review (keys-filtered when --keys given)
145
+ const heading = fileType === "requirements"
146
+ ? "Requirements under review"
147
+ : "Test file under review";
148
+ console.log(`\n## ${heading} (${filePath})\n`);
149
+ console.log(fileContentsToCheck);
150
+ if (scopeNote) {
151
+ console.log(scopeNote);
152
+ }
153
+ }
154
+ /**
155
+ * CLI-STYLE-3: --source cloud — send the file to the hosted review endpoint
156
+ * and print the returned feedback. Requires cloud credentials.
157
+ */
158
+ async function runCloudStyleCheck(params) {
159
+ const { workspaceRoot, filePath, fileContentsToCheck, fileType, model, scopeLabel, scopeNote, } = params;
160
+ // CLI-STYLE-3.2: credentials are required
39
161
  let projectId;
40
162
  let projectSecret;
41
163
  try {
@@ -47,29 +169,32 @@ export async function styleCheckCommand(filePath, options) {
47
169
  throw new Error(`Style check requires cloud credentials. Run \`dotrequirements link\` to connect this project to the cloud.\n` +
48
170
  `(${error instanceof Error ? error.message : String(error)})`);
49
171
  }
50
- // CLI-STYLE-1.4 / .8 / .9: dispatch to the hosted endpoint
172
+ // CLI-STYLE-3.0 / .1 / .3: dispatch to the hosted endpoint
51
173
  const apiBaseUrl = process.env.DOTREQUIREMENTS_API_URL ?? DEFAULT_API_BASE_URL;
52
174
  let feedback;
175
+ let quotaWarning;
53
176
  try {
54
- feedback = await fetchStyleCheckFeedback({
177
+ ({ feedback, quotaWarning } = await fetchStyleCheckFeedback({
55
178
  apiBaseUrl,
56
179
  projectId,
57
180
  projectSecret,
58
181
  fileContents: fileContentsToCheck,
59
182
  fileType,
60
- model: options.model,
61
- });
183
+ model,
184
+ }));
62
185
  }
63
186
  catch (error) {
64
187
  throw new Error(`Style check failed: ${error instanceof Error ? error.message : String(error)}`);
65
188
  }
66
- const scopeLabel = options.keys && options.keys.length > 0
67
- ? ` (${options.keys.join(", ")})`
68
- : "";
189
+ // CLI-STYLE-3.4: hosted feedback arrives severity-categorized
69
190
  console.log(`Style Check Results for ${filePath}${scopeLabel}\n`);
70
191
  console.log(feedback);
71
192
  if (scopeNote) {
72
193
  console.log(scopeNote);
73
194
  }
195
+ // LIMITS-5.2 / 5.2.0: the 80–99% quota warning prints with the result
196
+ if (quotaWarning) {
197
+ console.log(`\n⚠ ${quotaWarning}`);
198
+ }
74
199
  }
75
200
  //# sourceMappingURL=style-check.js.map
package/dist/convex.d.ts CHANGED
@@ -55,11 +55,9 @@ export declare const api: {
55
55
  };
56
56
  mutations: {
57
57
  createInvite: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
58
+ acceptInvite: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
58
59
  };
59
60
  };
60
- polar: {
61
- acceptTeamInvite: import("convex/server").FunctionReference<"action", "public", any, any, string | undefined>;
62
- };
63
61
  projectSecrets: {
64
62
  queries: {
65
63
  getOwnSecret: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
package/dist/convex.js CHANGED
@@ -61,11 +61,11 @@ export const api = {
61
61
  },
62
62
  mutations: {
63
63
  createInvite: mutation("teamInvites/mutations:createInvite"),
64
+ // Joins the team and reconciles paid-team seats via a scheduled sync
65
+ // (POLAR-SEATS-3). Replaced the deleted polar:acceptTeamInvite action.
66
+ acceptInvite: mutation("teamInvites/mutations:acceptInvite"),
64
67
  },
65
68
  },
66
- polar: {
67
- acceptTeamInvite: action("polar:acceptTeamInvite"),
68
- },
69
69
  projectSecrets: {
70
70
  queries: {
71
71
  getOwnSecret: query("projectSecrets/queries:getOwnSecret"),
@@ -22,11 +22,23 @@ export interface StyleCheckParams {
22
22
  model?: string;
23
23
  }
24
24
  /**
25
- * POST to `/api/style-check`. Returns the feedback text on success;
26
- * throws an Error with the underlying failure reason on a non-OK
27
- * response or a network error.
25
+ * Feedback plus the optional LIMITS-5.2 quota warning ("You've used X% of
26
+ * your AI quota this billing cycle."). The warning is present when the
27
+ * team is at 80–99% of its AI allowance; callers print it alongside the
28
+ * feedback so the eventual quota wall never arrives unannounced. Older
29
+ * servers simply omit the field.
28
30
  */
29
- export declare function fetchStyleCheckFeedback(params: StyleCheckParams): Promise<string>;
31
+ export interface CloudAIFeedback {
32
+ feedback: string;
33
+ quotaWarning: string | null;
34
+ }
35
+ /**
36
+ * POST to `/api/style-check`. Returns the feedback (and any quota warning)
37
+ * on success; throws an Error with the underlying failure reason on a
38
+ * non-OK response or a network error — including the LIMITS-5.1
39
+ * quota-exceeded error, whose message carries the billing-page upgrade link.
40
+ */
41
+ export declare function fetchStyleCheckFeedback(params: StyleCheckParams): Promise<CloudAIFeedback>;
30
42
  export interface ReviewTestParams {
31
43
  apiBaseUrl: string;
32
44
  projectId: string;
@@ -39,11 +51,12 @@ export interface ReviewTestParams {
39
51
  }>;
40
52
  }
41
53
  /**
42
- * POST to `/api/review-test`. Returns the feedback text on success;
43
- * throws an Error with the underlying failure reason on a non-OK
44
- * response or a network error.
54
+ * POST to `/api/review-test`. Returns the feedback (and any quota warning)
55
+ * on success; throws an Error with the underlying failure reason on a
56
+ * non-OK response or a network error — including the LIMITS-5.1
57
+ * quota-exceeded error, whose message carries the billing-page upgrade link.
45
58
  */
46
- export declare function fetchReviewTestFeedback(params: ReviewTestParams): Promise<string>;
59
+ export declare function fetchReviewTestFeedback(params: ReviewTestParams): Promise<CloudAIFeedback>;
47
60
  export interface ProjectContext {
48
61
  projectContext: string | null;
49
62
  requirementsStyleContext: string | null;
@@ -14,9 +14,10 @@
14
14
  */
15
15
  export const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
16
16
  /**
17
- * POST to `/api/style-check`. Returns the feedback text on success;
18
- * throws an Error with the underlying failure reason on a non-OK
19
- * response or a network error.
17
+ * POST to `/api/style-check`. Returns the feedback (and any quota warning)
18
+ * on success; throws an Error with the underlying failure reason on a
19
+ * non-OK response or a network error — including the LIMITS-5.1
20
+ * quota-exceeded error, whose message carries the billing-page upgrade link.
20
21
  */
21
22
  export async function fetchStyleCheckFeedback(params) {
22
23
  const response = await fetch(`${params.apiBaseUrl}/api/style-check`, {
@@ -35,12 +36,13 @@ export async function fetchStyleCheckFeedback(params) {
35
36
  throw new Error(errorData.error || response.statusText);
36
37
  }
37
38
  const data = (await response.json());
38
- return data.feedback;
39
+ return { feedback: data.feedback, quotaWarning: data.quotaWarning ?? null };
39
40
  }
40
41
  /**
41
- * POST to `/api/review-test`. Returns the feedback text on success;
42
- * throws an Error with the underlying failure reason on a non-OK
43
- * response or a network error.
42
+ * POST to `/api/review-test`. Returns the feedback (and any quota warning)
43
+ * on success; throws an Error with the underlying failure reason on a
44
+ * non-OK response or a network error — including the LIMITS-5.1
45
+ * quota-exceeded error, whose message carries the billing-page upgrade link.
44
46
  */
45
47
  export async function fetchReviewTestFeedback(params) {
46
48
  const response = await fetch(`${params.apiBaseUrl}/api/review-test`, {
@@ -58,7 +60,7 @@ export async function fetchReviewTestFeedback(params) {
58
60
  throw new Error(errorData.error || response.statusText);
59
61
  }
60
62
  const data = (await response.json());
61
- return data.feedback;
63
+ return { feedback: data.feedback, quotaWarning: data.quotaWarning ?? null };
62
64
  }
63
65
  /**
64
66
  * Query Convex for project-level AI context (project description and
@@ -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