@popoverai/dotrequirements 0.22.0 → 0.24.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.
Files changed (230) hide show
  1. package/README.md +173 -23
  2. package/dist/cli.js +121 -61
  3. package/dist/codebase-to-spec/budget.d.ts +53 -0
  4. package/dist/codebase-to-spec/budget.js +80 -0
  5. package/dist/codebase-to-spec/cache.d.ts +49 -0
  6. package/dist/codebase-to-spec/cache.js +54 -0
  7. package/dist/codebase-to-spec/claude.d.ts +69 -0
  8. package/dist/codebase-to-spec/claude.js +126 -0
  9. package/dist/codebase-to-spec/compose.d.ts +49 -0
  10. package/dist/codebase-to-spec/compose.js +124 -0
  11. package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
  12. package/dist/codebase-to-spec/edit-loop.js +195 -0
  13. package/dist/codebase-to-spec/editor.d.ts +54 -0
  14. package/dist/codebase-to-spec/editor.js +74 -0
  15. package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
  16. package/dist/codebase-to-spec/exit-codes.js +58 -0
  17. package/dist/codebase-to-spec/fan-out.d.ts +63 -0
  18. package/dist/codebase-to-spec/fan-out.js +215 -0
  19. package/dist/codebase-to-spec/interactive.d.ts +30 -0
  20. package/dist/codebase-to-spec/interactive.js +48 -0
  21. package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
  22. package/dist/codebase-to-spec/outline-review-loop.js +187 -0
  23. package/dist/codebase-to-spec/pack.d.ts +51 -0
  24. package/dist/codebase-to-spec/pack.js +127 -0
  25. package/dist/codebase-to-spec/planner.d.ts +41 -0
  26. package/dist/codebase-to-spec/planner.js +76 -0
  27. package/dist/codebase-to-spec/present.d.ts +94 -0
  28. package/dist/codebase-to-spec/present.js +288 -0
  29. package/dist/codebase-to-spec/progress.d.ts +33 -0
  30. package/dist/codebase-to-spec/progress.js +28 -0
  31. package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
  32. package/dist/codebase-to-spec/prompts/editor.js +57 -0
  33. package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
  34. package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
  35. package/dist/codebase-to-spec/prompts/planner-apply.d.ts +12 -0
  36. package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
  37. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
  38. package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
  39. package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
  40. package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
  41. package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
  42. package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
  43. package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
  44. package/dist/codebase-to-spec/prompts/specifier.js +100 -0
  45. package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
  46. package/dist/codebase-to-spec/prompts/style-check.js +78 -0
  47. package/dist/codebase-to-spec/schemas.d.ts +257 -0
  48. package/dist/codebase-to-spec/schemas.js +183 -0
  49. package/dist/codebase-to-spec/skill-install.d.ts +57 -0
  50. package/dist/codebase-to-spec/skill-install.js +79 -0
  51. package/dist/codebase-to-spec/slice.d.ts +49 -0
  52. package/dist/codebase-to-spec/slice.js +111 -0
  53. package/dist/codebase-to-spec/specifier.d.ts +60 -0
  54. package/dist/codebase-to-spec/specifier.js +79 -0
  55. package/dist/codebase-to-spec/style-check.d.ts +29 -0
  56. package/dist/codebase-to-spec/style-check.js +33 -0
  57. package/dist/codebase-to-spec/summary.d.ts +51 -0
  58. package/dist/codebase-to-spec/summary.js +183 -0
  59. package/dist/codebase-to-spec/validate.d.ts +46 -0
  60. package/dist/codebase-to-spec/validate.js +130 -0
  61. package/dist/commands/acceptance-test.d.ts +6 -0
  62. package/dist/commands/acceptance-test.js +212 -0
  63. package/dist/commands/ai-setup.d.ts +5 -0
  64. package/dist/commands/ai-setup.js +441 -0
  65. package/dist/commands/browsertest.d.ts +0 -1
  66. package/dist/commands/browsertest.js +51 -26
  67. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  68. package/dist/commands/codebase-to-spec/compose.js +57 -0
  69. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  70. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  71. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  72. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  73. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  74. package/dist/commands/codebase-to-spec/index.js +135 -0
  75. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  76. package/dist/commands/codebase-to-spec/pack.js +76 -0
  77. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  78. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  79. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  80. package/dist/commands/codebase-to-spec/present.js +92 -0
  81. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  82. package/dist/commands/codebase-to-spec/run.js +85 -0
  83. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  84. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  85. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  86. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  87. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  88. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  89. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  90. package/dist/commands/codebase-to-spec/validate.js +38 -0
  91. package/dist/commands/create-requirement-document.d.ts +2 -0
  92. package/dist/commands/create-requirement-document.js +41 -0
  93. package/dist/commands/finalize.js +7 -7
  94. package/dist/commands/get.d.ts +2 -0
  95. package/dist/commands/get.js +55 -0
  96. package/dist/commands/init.js +132 -117
  97. package/dist/commands/link.js +27 -27
  98. package/dist/commands/list.d.ts +6 -0
  99. package/dist/commands/list.js +43 -0
  100. package/dist/commands/mcp-setup.js +159 -149
  101. package/dist/commands/mcp.js +1 -1
  102. package/dist/commands/prepare.js +4 -4
  103. package/dist/commands/pull.js +116 -121
  104. package/dist/commands/push.js +106 -112
  105. package/dist/commands/report.d.ts +6 -2
  106. package/dist/commands/report.js +177 -122
  107. package/dist/commands/requirements-for.d.ts +2 -0
  108. package/dist/commands/requirements-for.js +29 -0
  109. package/dist/commands/review-test.d.ts +2 -0
  110. package/dist/commands/review-test.js +75 -0
  111. package/dist/commands/search.d.ts +6 -0
  112. package/dist/commands/search.js +39 -0
  113. package/dist/commands/style-check.d.ts +7 -0
  114. package/dist/commands/style-check.js +75 -0
  115. package/dist/commands/test.js +53 -59
  116. package/dist/commands/tests-for.d.ts +2 -0
  117. package/dist/commands/tests-for.js +80 -0
  118. package/dist/commands/validate.d.ts +6 -0
  119. package/dist/commands/validate.js +72 -0
  120. package/dist/config.js +1 -1
  121. package/dist/convex.d.ts +34 -22
  122. package/dist/convex.js +38 -22
  123. package/dist/harness/cache.d.ts +1 -5
  124. package/dist/harness/cache.js +49 -59
  125. package/dist/harness/convexReporting.d.ts +1 -1
  126. package/dist/harness/convexReporting.js +9 -7
  127. package/dist/harness/coverageCache.js +3 -3
  128. package/dist/harness/finalize.js +59 -46
  129. package/dist/harness/index.d.ts +6 -7
  130. package/dist/harness/index.js +9 -10
  131. package/dist/harness/prepare.js +6 -5
  132. package/dist/harness/requirementsLoader.d.ts +2 -2
  133. package/dist/harness/requirementsLoader.js +13 -35
  134. package/dist/harness/tracking.js +18 -18
  135. package/dist/harness/types.d.ts +1 -1
  136. package/dist/mcp/convexClient.d.ts +0 -39
  137. package/dist/mcp/convexClient.js +2 -107
  138. package/dist/mcp/grep.d.ts +1 -1
  139. package/dist/mcp/grep.js +87 -42
  140. package/dist/mcp/handlers/authoring.d.ts +1 -1
  141. package/dist/mcp/handlers/authoring.js +30 -234
  142. package/dist/mcp/handlers/coverage.d.ts +1 -1
  143. package/dist/mcp/handlers/coverage.js +13 -15
  144. package/dist/mcp/handlers/debug.d.ts +2 -3
  145. package/dist/mcp/handlers/debug.js +10 -10
  146. package/dist/mcp/handlers/get.d.ts +1 -1
  147. package/dist/mcp/handlers/get.js +11 -10
  148. package/dist/mcp/handlers/index.d.ts +20 -20
  149. package/dist/mcp/handlers/index.js +10 -10
  150. package/dist/mcp/handlers/list.d.ts +4 -33
  151. package/dist/mcp/handlers/list.js +16 -38
  152. package/dist/mcp/handlers/push.d.ts +1 -1
  153. package/dist/mcp/handlers/push.js +28 -18
  154. package/dist/mcp/handlers/report.d.ts +16 -0
  155. package/dist/mcp/handlers/report.js +134 -0
  156. package/dist/mcp/handlers/review.d.ts +1 -1
  157. package/dist/mcp/handlers/review.js +40 -59
  158. package/dist/mcp/handlers/search.d.ts +1 -1
  159. package/dist/mcp/handlers/search.js +7 -9
  160. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  161. package/dist/mcp/handlers/test-mapping.js +14 -14
  162. package/dist/mcp/handlers/types.d.ts +3 -3
  163. package/dist/mcp/handlers/types.js +2 -2
  164. package/dist/mcp/index.d.ts +1 -1
  165. package/dist/mcp/index.js +156 -178
  166. package/dist/mcp/requirements.d.ts +2 -2
  167. package/dist/mcp/requirements.js +30 -30
  168. package/dist/mcp/testCodeExtractor.js +24 -26
  169. package/dist/mcp/types.d.ts +1 -1
  170. package/dist/push/core.d.ts +2 -2
  171. package/dist/push/core.js +20 -20
  172. package/dist/push/index.d.ts +1 -1
  173. package/dist/push/index.js +2 -2
  174. package/dist/requirements/cloud-ai.d.ts +57 -0
  175. package/dist/requirements/cloud-ai.js +104 -0
  176. package/dist/requirements/cloud-coverage.d.ts +41 -0
  177. package/dist/requirements/cloud-coverage.js +60 -0
  178. package/dist/requirements/coverage.d.ts +45 -0
  179. package/dist/requirements/coverage.js +114 -0
  180. package/dist/requirements/grep.d.ts +33 -0
  181. package/dist/requirements/grep.js +306 -0
  182. package/dist/requirements/index.d.ts +73 -0
  183. package/dist/requirements/index.js +174 -0
  184. package/dist/requirements/style-guide.d.ts +67 -0
  185. package/dist/requirements/style-guide.js +299 -0
  186. package/dist/requirements/testCodeExtractor.d.ts +22 -0
  187. package/dist/requirements/testCodeExtractor.js +150 -0
  188. package/dist/schema/browser.d.ts +8 -6
  189. package/dist/schema/browser.js +14 -14
  190. package/dist/schema/builder.d.ts +1 -1
  191. package/dist/schema/builder.js +13 -44
  192. package/dist/schema/conversions.d.ts +2 -2
  193. package/dist/schema/conversions.js +11 -11
  194. package/dist/schema/index.d.ts +9 -7
  195. package/dist/schema/index.js +15 -13
  196. package/dist/schema/parser-core.d.ts +1 -1
  197. package/dist/schema/parser-core.js +23 -22
  198. package/dist/schema/parser.d.ts +3 -3
  199. package/dist/schema/parser.js +27 -31
  200. package/dist/schema/resolver.d.ts +1 -1
  201. package/dist/schema/resolver.js +9 -9
  202. package/dist/schema/scenario.d.ts +91 -0
  203. package/dist/schema/scenario.js +82 -0
  204. package/dist/schema/schemas.d.ts +3 -3
  205. package/dist/schema/schemas.js +41 -28
  206. package/dist/schema/test-schema.js +27 -27
  207. package/dist/templates/context-file-section.md +3 -2
  208. package/dist/templates/example-requirements.js +1 -1
  209. package/dist/templates/example-requirements.ts +3 -1
  210. package/dist/templates/requirements-readme.js +1 -1
  211. package/dist/templates/requirements-readme.ts +1 -1
  212. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  213. package/dist/utils/brand.js +3 -3
  214. package/dist/utils/browser-launch.js +4 -4
  215. package/dist/utils/context-file.d.ts +1 -1
  216. package/dist/utils/context-file.js +26 -26
  217. package/dist/utils/env.js +7 -7
  218. package/dist/utils/gitignore.js +7 -7
  219. package/dist/utils/oauth-callback-server.d.ts +1 -1
  220. package/dist/utils/oauth-callback-server.js +27 -25
  221. package/dist/utils/oauth-flow.js +32 -29
  222. package/dist/utils/project-discovery.d.ts +3 -3
  223. package/dist/utils/project-discovery.js +18 -17
  224. package/dist/utils/project-name.js +8 -8
  225. package/dist/utils/project-selector.d.ts +1 -1
  226. package/dist/utils/project-selector.js +24 -21
  227. package/dist/utils/project-settings.d.ts +5 -4
  228. package/dist/utils/project-settings.js +28 -20
  229. package/dist/utils/templates.js +6 -6
  230. package/package.json +2 -1
@@ -1,130 +1,125 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import { ConvexHttpClient } from 'convex/browser';
4
- import { getConvexUrl } from '../config.js';
5
- import { buildRequirementsFile } from '../schema/index.js';
6
- import { api } from '../convex.js';
7
- import { brand } from '../utils/brand.js';
8
- import { findRequirementsFiles } from '../mcp/requirements.js';
9
- import { getProjectCredentials } from '../utils/project-settings.js';
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { ConvexHttpClient } from "convex/browser";
4
+ import { getConvexUrl } from "../config.js";
5
+ import { api } from "../convex.js";
6
+ import { findRequirementsFiles } from "../requirements/index.js";
7
+ import { buildRequirementsFile } from "../schema/index.js";
8
+ import { brand } from "../utils/brand.js";
9
+ import { getProjectCredentials } from "../utils/project-settings.js";
10
10
  export async function pullCommand(options) {
11
- try {
12
- // Connect to Convex
13
- const convexUrl = getConvexUrl();
14
- const client = new ConvexHttpClient(convexUrl);
15
- let documents;
16
- let projectName;
17
- let isShareTokenFlow = false;
18
- // SHARE-TOKEN-CLI-1: Handle share token flow (no project config needed)
19
- if (options.share) {
20
- isShareTokenFlow = true;
21
- console.log(`Pulling requirements with share token...`);
22
- try {
23
- // SHARE-TOKEN-CLI-1.1: Pass only projectSecret, no projectSlug
24
- // The authzQueryReadOnly wrapper will resolve project from the token
25
- const result = await client.query(api.documents.queries.exportProjectForCli, {
26
- projectAuth: {
27
- projectSecret: options.share,
28
- },
29
- });
30
- documents = result.documents;
31
- projectName = result.projectName;
32
- // SHARE-TOKEN-CLI-1.2: Display project name in output
33
- console.log(`\nProject: "${projectName}"`);
34
- }
35
- catch (error) {
36
- // SHARE-TOKEN-CLI-2: Handle invalid/revoked tokens gracefully
37
- const errorMessage = error instanceof Error ? error.message : String(error);
38
- if (errorMessage.includes('Invalid share token')) {
39
- throw new Error('Invalid share token. The token may be incorrect or has been revoked.\n' +
40
- 'Please request a new share token from your team.');
41
- }
42
- throw error;
43
- }
11
+ // Connect to Convex
12
+ const convexUrl = getConvexUrl();
13
+ const client = new ConvexHttpClient(convexUrl);
14
+ let documents;
15
+ let projectName;
16
+ let isShareTokenFlow = false;
17
+ // SHARE-TOKEN-CLI-1: Handle share token flow (no project config needed)
18
+ if (options.share) {
19
+ isShareTokenFlow = true;
20
+ console.log(`Pulling requirements with share token...`);
21
+ try {
22
+ // SHARE-TOKEN-CLI-1.1: Pass only projectSecret, no projectSlug
23
+ // The authzQueryReadOnly wrapper will resolve project from the token
24
+ const result = await client.query(api.documents.queries.exportProjectForCli, {
25
+ projectAuth: {
26
+ projectSecret: options.share,
27
+ },
28
+ });
29
+ documents = result.documents;
30
+ projectName = result.projectName;
31
+ // SHARE-TOKEN-CLI-1.2: Display project name in output
32
+ console.log(`\nProject: "${projectName}"`);
44
33
  }
45
- else {
46
- console.log(`Syncing requirements from ${brand} cloud...`);
47
- // Get project credentials from .requirements/project-settings.json
48
- // This throws helpful errors if project not found or not connected to cloud
49
- const credentials = getProjectCredentials();
50
- // Allow --project flag to override, but still need secret from settings
51
- const projectId = options.project ?? credentials.projectId;
52
- const projectSecret = credentials.projectSecret;
53
- // Fetch documents
54
- if (options.document) {
55
- // Fetch specific document
56
- const doc = await client.query(api.documents.queries.exportForCli, {
57
- projectAuth: {
58
- projectSlug: projectId,
59
- projectSecret,
60
- },
61
- target: { type: 'document', id: options.document },
62
- documentId: options.document,
63
- });
64
- documents = doc ? [doc] : [];
65
- }
66
- else if (projectId) {
67
- // Fetch all documents for project
68
- // SYNC-ARCH-3.1: Use projectSlug (not projectId) since env stores the slug
69
- const result = await client.query(api.documents.queries.exportProjectForCli, {
70
- projectAuth: {
71
- projectSlug: projectId,
72
- projectSecret,
73
- },
74
- });
75
- documents = result.documents;
76
- projectName = result.projectName;
77
- }
78
- else {
79
- throw new Error('Either --project or --document must be specified');
34
+ catch (error) {
35
+ // SHARE-TOKEN-CLI-2: Handle invalid/revoked tokens gracefully
36
+ const errorMessage = error instanceof Error ? error.message : String(error);
37
+ if (errorMessage.includes("Invalid share token")) {
38
+ throw new Error("Invalid share token. The token may be incorrect or has been revoked.\n" +
39
+ "Please request a new share token from your team.");
80
40
  }
41
+ throw error;
81
42
  }
82
- if (documents.length === 0) {
83
- console.log('No documents found.');
84
- return;
85
- }
86
- // Ensure .requirements/ directory exists (for new documents)
87
- const requirementsDir = path.join(process.cwd(), '.requirements');
88
- if (!fs.existsSync(requirementsDir)) {
89
- fs.mkdirSync(requirementsDir, { recursive: true });
43
+ }
44
+ else {
45
+ console.log(`Syncing requirements from ${brand} cloud...`);
46
+ // Get project credentials from .requirements/project-settings.json
47
+ // This throws helpful errors if project not found or not connected to cloud
48
+ const credentials = getProjectCredentials();
49
+ // Allow --project flag to override, but still need secret from settings
50
+ const projectId = options.project ?? credentials.projectId;
51
+ const projectSecret = credentials.projectSecret;
52
+ // Fetch documents
53
+ if (options.document) {
54
+ // Fetch specific document
55
+ const doc = await client.query(api.documents.queries.exportForCli, {
56
+ projectAuth: {
57
+ projectSlug: projectId,
58
+ projectSecret,
59
+ },
60
+ target: { type: "document", id: options.document },
61
+ documentId: options.document,
62
+ });
63
+ documents = doc ? [doc] : [];
90
64
  }
91
- // Build index of existing files by document ID (search entire workspace)
92
- const existingFilesByDocId = await buildDocumentIdIndex(process.cwd());
93
- // Write each document as a Markdown file
94
- for (const doc of documents) {
95
- // Check if an existing file has this document ID
96
- const existingFilePath = existingFilesByDocId.get(doc.documentId);
97
- const filePath = existingFilePath ?? path.join(requirementsDir, sanitizeFileName(doc.title) + '.requirements.md');
98
- const fileName = path.basename(filePath);
99
- // Build metadata with pulledAt for conflict detection
100
- // SYNC-CLI-EDIT-2: pulledAt is compared with cloud updatedAt during push
101
- const metadata = {
102
- pulledAt: new Date().toISOString(),
103
- version: doc.version,
104
- document: {
105
- id: doc.documentId,
106
- title: doc.title,
107
- defaultPrefix: doc.defaultPrefix,
65
+ else if (projectId) {
66
+ // Fetch all documents for project
67
+ // SYNC-ARCH-3.1: Use projectSlug (not projectId) since env stores the slug
68
+ const result = await client.query(api.documents.queries.exportProjectForCli, {
69
+ projectAuth: {
70
+ projectSlug: projectId,
71
+ projectSecret,
108
72
  },
109
- };
110
- // SYNC-ARCH-1: Use markdownContent directly from cloud (source of truth)
111
- // No need to rebuild from requirements - just combine with frontmatter
112
- const fileContent = buildRequirementsFile(metadata, doc.markdownContent);
113
- fs.writeFileSync(filePath, fileContent, 'utf-8');
114
- console.log(`✓ Synced: ${fileName} (version ${doc.version})`);
73
+ });
74
+ documents = result.documents;
75
+ projectName = result.projectName;
115
76
  }
116
- console.log(`\nSuccessfully synced ${documents.length} document(s) to .requirements/`);
117
- // SHARE-TOKEN-CLI-3: Show next steps for share token users
118
- if (isShareTokenFlow) {
119
- console.log('\n─────────────────────────────────────────────────────────');
120
- console.log('Next steps to get full access (push, coverage):');
121
- console.log(' Run: dotrequirements link');
122
- console.log('─────────────────────────────────────────────────────────');
77
+ else {
78
+ throw new Error("Either --project or --document must be specified");
123
79
  }
124
80
  }
125
- catch (error) {
126
- // Re-throw to let the CLI error handler display it cleanly
127
- throw error;
81
+ if (documents.length === 0) {
82
+ console.log("No documents found.");
83
+ return;
84
+ }
85
+ // Ensure .requirements/ directory exists (for new documents)
86
+ const requirementsDir = path.join(process.cwd(), ".requirements");
87
+ if (!fs.existsSync(requirementsDir)) {
88
+ fs.mkdirSync(requirementsDir, { recursive: true });
89
+ }
90
+ // Build index of existing files by document ID (search entire workspace)
91
+ const existingFilesByDocId = await buildDocumentIdIndex(process.cwd());
92
+ // Write each document as a Markdown file
93
+ for (const doc of documents) {
94
+ // Check if an existing file has this document ID
95
+ const existingFilePath = existingFilesByDocId.get(doc.documentId);
96
+ const filePath = existingFilePath ??
97
+ path.join(requirementsDir, `${sanitizeFileName(doc.title)}.requirements.md`);
98
+ const fileName = path.basename(filePath);
99
+ // Build metadata with pulledAt for conflict detection
100
+ // SYNC-CLI-EDIT-2: pulledAt is compared with cloud updatedAt during push
101
+ const metadata = {
102
+ pulledAt: new Date().toISOString(),
103
+ version: doc.version,
104
+ document: {
105
+ id: doc.documentId,
106
+ title: doc.title,
107
+ defaultPrefix: doc.defaultPrefix,
108
+ },
109
+ };
110
+ // SYNC-ARCH-1: Use markdownContent directly from cloud (source of truth)
111
+ // No need to rebuild from requirements - just combine with frontmatter
112
+ const fileContent = buildRequirementsFile(metadata, doc.markdownContent);
113
+ fs.writeFileSync(filePath, fileContent, "utf-8");
114
+ console.log(`✓ Synced: ${fileName} (version ${doc.version})`);
115
+ }
116
+ console.log(`\nSuccessfully synced ${documents.length} document(s) to .requirements/`);
117
+ // SHARE-TOKEN-CLI-3: Show next steps for share token users
118
+ if (isShareTokenFlow) {
119
+ console.log("\n─────────────────────────────────────────────────────────");
120
+ console.log("Next steps to get full access (push, coverage):");
121
+ console.log(" Run: dotrequirements link");
122
+ console.log("─────────────────────────────────────────────────────────");
128
123
  }
129
124
  }
130
125
  /**
@@ -133,8 +128,8 @@ export async function pullCommand(options) {
133
128
  function sanitizeFileName(title) {
134
129
  return title
135
130
  .toLowerCase()
136
- .replace(/[^a-z0-9]+/g, '-')
137
- .replace(/^-+|-+$/g, '');
131
+ .replace(/[^a-z0-9]+/g, "-")
132
+ .replace(/^-+|-+$/g, "");
138
133
  }
139
134
  /**
140
135
  * Build an index mapping document IDs to their file paths.
@@ -159,7 +154,7 @@ async function buildDocumentIdIndex(workspaceRoot) {
159
154
  */
160
155
  function extractDocumentIdFromFile(filePath) {
161
156
  try {
162
- const content = fs.readFileSync(filePath, 'utf-8');
157
+ const content = fs.readFileSync(filePath, "utf-8");
163
158
  // Match document.id in YAML frontmatter - handles both inline and nested formats
164
159
  // Inline: document: { id: "abc123", ... }
165
160
  // Nested (id: can appear at any position within the indented document block):
@@ -1,120 +1,114 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as readline from 'readline';
4
- import { brand } from '../utils/brand.js';
5
- import { findRequirementsFiles } from '../mcp/requirements.js';
6
- import { getProjectCredentials } from '../utils/project-settings.js';
7
- import { getConvexUrl } from '../config.js';
8
- import { parseFilesForPush, dryRunPush, executePush, } from '../push/index.js';
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import * as readline from "node:readline";
4
+ import { getConvexUrl } from "../config.js";
5
+ import { dryRunPush, executePush, parseFilesForPush, } from "../push/index.js";
6
+ import { findRequirementsFiles } from "../requirements/index.js";
7
+ import { brand } from "../utils/brand.js";
8
+ import { getProjectCredentials } from "../utils/project-settings.js";
9
9
  export async function pushCommand(file, options) {
10
- try {
11
- console.log(`Preparing to push requirements to ${brand} cloud...\n`);
12
- // Get project credentials from .requirements/project-settings.json
13
- // This throws helpful errors if project not found or not connected to cloud
14
- const { projectId, projectSecret } = getProjectCredentials();
15
- // Determine which files to push
16
- let filesToPush;
17
- if (file) {
18
- // Push specific file
19
- const filePath = path.resolve(file);
20
- if (!fs.existsSync(filePath)) {
21
- throw new Error(`File not found: ${file}`);
22
- }
23
- filesToPush = [filePath];
10
+ console.log(`Preparing to push requirements to ${brand} cloud...\n`);
11
+ // Get project credentials from .requirements/project-settings.json
12
+ // This throws helpful errors if project not found or not connected to cloud
13
+ const { projectId, projectSecret } = getProjectCredentials();
14
+ // Determine which files to push
15
+ let filesToPush;
16
+ if (file) {
17
+ // Push specific file
18
+ const filePath = path.resolve(file);
19
+ if (!fs.existsSync(filePath)) {
20
+ throw new Error(`File not found: ${file}`);
24
21
  }
25
- else {
26
- // SYNC-DISCOVERY-1.0: Push all *.requirements.md files in workspace (not just .requirements/)
27
- const workspaceRoot = process.cwd();
28
- filesToPush = await findRequirementsFiles(workspaceRoot);
29
- if (filesToPush.length === 0) {
30
- console.log('No *.requirements.md files found. Nothing to push.');
31
- return;
32
- }
33
- }
34
- // Parse all local files
35
- console.log('Parsing local requirements...');
36
- const { parsedFiles, totalRequirements } = parseFilesForPush(filesToPush);
37
- // Log file parsing progress
38
- for (const file of parsedFiles) {
39
- const fileName = path.basename(file.filePath);
40
- const doc = file.metadata.document;
41
- if (doc?.defaultPrefix && !file.metadata.document?.defaultPrefix) {
42
- console.log(` Reading ${fileName}...`);
43
- console.log(` Inferred defaultPrefix "${doc.defaultPrefix}" from first requirement`);
44
- }
45
- else {
46
- console.log(` Reading ${fileName}...`);
47
- }
48
- }
49
- console.log(`\nFound ${totalRequirements} requirement(s) in ${parsedFiles.length} document(s).\n`);
50
- // Build credentials
51
- const credentials = {
52
- projectId,
53
- projectSecret,
54
- convexUrl: getConvexUrl(),
55
- };
56
- // Phase 1: Dry run
57
- console.log('Validating documents...');
58
- const dryRunResult = await dryRunPush(parsedFiles, credentials);
59
- // Display unified summary
60
- displayDryRunSummary(dryRunResult);
61
- // Check if there's anything to push
62
- const pushableCount = dryRunResult.updates.length +
63
- dryRunResult.creates.length +
64
- dryRunResult.notFound.length;
65
- if (pushableCount === 0) {
66
- console.log('No valid documents to push.');
22
+ filesToPush = [filePath];
23
+ }
24
+ else {
25
+ // SYNC-DISCOVERY-1.0: Push all *.requirements.md files in workspace (not just .requirements/)
26
+ const workspaceRoot = process.cwd();
27
+ filesToPush = await findRequirementsFiles(workspaceRoot);
28
+ if (filesToPush.length === 0) {
29
+ console.log("No *.requirements.md files found. Nothing to push.");
67
30
  return;
68
31
  }
69
- // Single confirmation prompt
70
- if (!options.yes) {
71
- const hasConflicts = dryRunResult.conflicts.length > 0;
72
- const prompt = hasConflicts
73
- ? 'Continue with push? (will overwrite cloud changes) [y/N] '
74
- : 'Continue with push? [y/N] ';
75
- const confirmed = await confirm(prompt);
76
- if (!confirmed) {
77
- console.log('Push cancelled.');
78
- return;
79
- }
32
+ }
33
+ // Parse all local files
34
+ console.log("Parsing local requirements...");
35
+ const { parsedFiles, totalRequirements } = parseFilesForPush(filesToPush);
36
+ // Log file parsing progress
37
+ for (const file of parsedFiles) {
38
+ const fileName = path.basename(file.filePath);
39
+ const doc = file.metadata.document;
40
+ if (doc?.defaultPrefix && !file.metadata.document?.defaultPrefix) {
41
+ console.log(` Reading ${fileName}...`);
42
+ console.log(` Inferred defaultPrefix "${doc.defaultPrefix}" from first requirement`);
80
43
  }
81
- // Phase 2: Execute push
82
- console.log('\nPushing documents to cloud...');
83
- const result = await executePush(dryRunResult, credentials);
84
- // Display results
85
- for (const { file, result: dryResult } of [
86
- ...dryRunResult.updates,
87
- ...dryRunResult.creates,
88
- ...dryRunResult.notFound,
89
- ]) {
90
- const fileName = path.basename(file.filePath);
91
- const isCreate = dryResult.action === 'create' || dryResult.action === 'not_found';
92
- // Check if this file had an error
93
- const error = result.errors.find((e) => e.fileName === fileName);
94
- if (error) {
95
- console.log(` ✗ Failed: ${fileName} - ${error.error}`);
96
- }
97
- else if (isCreate) {
98
- console.log(` ✓ Created: ${fileName} → ${file.metadata.document?.id}`);
99
- }
100
- else {
101
- console.log(` ✓ Updated: ${fileName}`);
102
- }
44
+ else {
45
+ console.log(` Reading ${fileName}...`);
103
46
  }
104
- console.log(`\n✓ Push complete!`);
105
- if (result.created > 0) {
106
- console.log(` Created: ${result.created} document(s)`);
47
+ }
48
+ console.log(`\nFound ${totalRequirements} requirement(s) in ${parsedFiles.length} document(s).\n`);
49
+ // Build credentials
50
+ const credentials = {
51
+ projectId,
52
+ projectSecret,
53
+ convexUrl: getConvexUrl(),
54
+ };
55
+ // Phase 1: Dry run
56
+ console.log("Validating documents...");
57
+ const dryRunResult = await dryRunPush(parsedFiles, credentials);
58
+ // Display unified summary
59
+ displayDryRunSummary(dryRunResult);
60
+ // Check if there's anything to push
61
+ const pushableCount = dryRunResult.updates.length +
62
+ dryRunResult.creates.length +
63
+ dryRunResult.notFound.length;
64
+ if (pushableCount === 0) {
65
+ console.log("No valid documents to push.");
66
+ return;
67
+ }
68
+ // Single confirmation prompt
69
+ if (!options.yes) {
70
+ const hasConflicts = dryRunResult.conflicts.length > 0;
71
+ const prompt = hasConflicts
72
+ ? "Continue with push? (will overwrite cloud changes) [y/N] "
73
+ : "Continue with push? [y/N] ";
74
+ const confirmed = await confirm(prompt);
75
+ if (!confirmed) {
76
+ console.log("Push cancelled.");
77
+ return;
78
+ }
79
+ }
80
+ // Phase 2: Execute push
81
+ console.log("\nPushing documents to cloud...");
82
+ const result = await executePush(dryRunResult, credentials);
83
+ // Display results
84
+ for (const { file, result: dryResult } of [
85
+ ...dryRunResult.updates,
86
+ ...dryRunResult.creates,
87
+ ...dryRunResult.notFound,
88
+ ]) {
89
+ const fileName = path.basename(file.filePath);
90
+ const isCreate = dryResult.action === "create" || dryResult.action === "not_found";
91
+ // Check if this file had an error
92
+ const error = result.errors.find((e) => e.fileName === fileName);
93
+ if (error) {
94
+ console.log(` ✗ Failed: ${fileName} - ${error.error}`);
107
95
  }
108
- if (result.updated > 0) {
109
- console.log(` Updated: ${result.updated} document(s)`);
96
+ else if (isCreate) {
97
+ console.log(` ✓ Created: ${fileName} → ${file.metadata.document?.id}`);
110
98
  }
111
- if (result.created === 0 && result.updated === 0) {
112
- console.log(' No documents were pushed.');
99
+ else {
100
+ console.log(` ✓ Updated: ${fileName}`);
113
101
  }
114
102
  }
115
- catch (error) {
116
- // Re-throw to let the CLI error handler display it cleanly
117
- throw error;
103
+ console.log(`\n✓ Push complete!`);
104
+ if (result.created > 0) {
105
+ console.log(` Created: ${result.created} document(s)`);
106
+ }
107
+ if (result.updated > 0) {
108
+ console.log(` Updated: ${result.updated} document(s)`);
109
+ }
110
+ if (result.created === 0 && result.updated === 0) {
111
+ console.log(" No documents were pushed.");
118
112
  }
119
113
  }
120
114
  /**
@@ -122,14 +116,14 @@ export async function pushCommand(file, options) {
122
116
  */
123
117
  function displayDryRunSummary(dryRunResult) {
124
118
  const { updates, creates, notFound, invalid, conflicts } = dryRunResult;
125
- console.log('\n=== Push Summary ===\n');
119
+ console.log("\n=== Push Summary ===\n");
126
120
  if (updates.length > 0) {
127
121
  console.log(`Updated documents (${updates.length}):`);
128
122
  for (const { file, result } of updates) {
129
123
  const fileName = path.basename(file.filePath);
130
124
  const hasConflict = conflicts.some((c) => c.item.file === file);
131
- const conflictNote = hasConflict ? ' ⚠️ [cloud changed since pull]' : '';
132
- const warningNote = result.warning ? ` (${result.warning})` : '';
125
+ const conflictNote = hasConflict ? " ⚠️ [cloud changed since pull]" : "";
126
+ const warningNote = result.warning ? ` (${result.warning})` : "";
133
127
  console.log(` ~ ${fileName}${conflictNote}${warningNote}`);
134
128
  }
135
129
  console.log();
@@ -138,7 +132,7 @@ function displayDryRunSummary(dryRunResult) {
138
132
  console.log(`New documents (${creates.length}):`);
139
133
  for (const { file, result } of creates) {
140
134
  const fileName = path.basename(file.filePath);
141
- const warningNote = result.warning ? ` (${result.warning})` : '';
135
+ const warningNote = result.warning ? ` (${result.warning})` : "";
142
136
  console.log(` + ${fileName}${warningNote}`);
143
137
  }
144
138
  console.log();
@@ -171,7 +165,7 @@ function confirm(question) {
171
165
  return new Promise((resolve) => {
172
166
  rl.question(question, (answer) => {
173
167
  rl.close();
174
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
168
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
175
169
  });
176
170
  });
177
171
  }
@@ -1,7 +1,11 @@
1
+ import { type CoverageEntry } from "../requirements/coverage.js";
1
2
  interface ReportOptions {
2
- format?: 'console' | 'json' | 'markdown';
3
+ source?: "local" | "cloud";
4
+ format?: "console" | "json" | "markdown";
3
5
  requirement?: string;
6
+ branch?: string;
7
+ since?: number;
4
8
  }
5
9
  export declare function reportCommand(options: ReportOptions): Promise<void>;
6
- export {};
10
+ export type { CoverageEntry };
7
11
  //# sourceMappingURL=report.d.ts.map