@popoverai/dotrequirements 0.23.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.
- package/README.md +167 -20
- package/dist/cli.js +121 -60
- package/dist/codebase-to-spec/budget.d.ts +53 -0
- package/dist/codebase-to-spec/budget.js +80 -0
- package/dist/codebase-to-spec/cache.d.ts +49 -0
- package/dist/codebase-to-spec/cache.js +54 -0
- package/dist/codebase-to-spec/claude.d.ts +69 -0
- package/dist/codebase-to-spec/claude.js +126 -0
- package/dist/codebase-to-spec/compose.d.ts +49 -0
- package/dist/codebase-to-spec/compose.js +124 -0
- package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
- package/dist/codebase-to-spec/edit-loop.js +195 -0
- package/dist/codebase-to-spec/editor.d.ts +54 -0
- package/dist/codebase-to-spec/editor.js +74 -0
- package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
- package/dist/codebase-to-spec/exit-codes.js +58 -0
- package/dist/codebase-to-spec/fan-out.d.ts +63 -0
- package/dist/codebase-to-spec/fan-out.js +215 -0
- package/dist/codebase-to-spec/interactive.d.ts +30 -0
- package/dist/codebase-to-spec/interactive.js +48 -0
- package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
- package/dist/codebase-to-spec/outline-review-loop.js +187 -0
- package/dist/codebase-to-spec/pack.d.ts +51 -0
- package/dist/codebase-to-spec/pack.js +127 -0
- package/dist/codebase-to-spec/planner.d.ts +41 -0
- package/dist/codebase-to-spec/planner.js +76 -0
- package/dist/codebase-to-spec/present.d.ts +94 -0
- package/dist/codebase-to-spec/present.js +288 -0
- package/dist/codebase-to-spec/progress.d.ts +33 -0
- package/dist/codebase-to-spec/progress.js +28 -0
- package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
- package/dist/codebase-to-spec/prompts/editor.js +57 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
- package/dist/codebase-to-spec/prompts/planner-apply.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
- package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
- package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
- package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
- package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
- package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/specifier.js +100 -0
- package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/style-check.js +78 -0
- package/dist/codebase-to-spec/schemas.d.ts +257 -0
- package/dist/codebase-to-spec/schemas.js +183 -0
- package/dist/codebase-to-spec/skill-install.d.ts +57 -0
- package/dist/codebase-to-spec/skill-install.js +79 -0
- package/dist/codebase-to-spec/slice.d.ts +49 -0
- package/dist/codebase-to-spec/slice.js +111 -0
- package/dist/codebase-to-spec/specifier.d.ts +60 -0
- package/dist/codebase-to-spec/specifier.js +79 -0
- package/dist/codebase-to-spec/style-check.d.ts +29 -0
- package/dist/codebase-to-spec/style-check.js +33 -0
- package/dist/codebase-to-spec/summary.d.ts +51 -0
- package/dist/codebase-to-spec/summary.js +183 -0
- package/dist/codebase-to-spec/validate.d.ts +46 -0
- package/dist/codebase-to-spec/validate.js +130 -0
- package/dist/commands/acceptance-test.d.ts +6 -0
- package/dist/commands/acceptance-test.js +212 -0
- package/dist/commands/ai-setup.d.ts +5 -0
- package/dist/commands/ai-setup.js +441 -0
- package/dist/commands/browsertest.js +34 -27
- package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
- package/dist/commands/codebase-to-spec/compose.js +57 -0
- package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
- package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
- package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
- package/dist/commands/codebase-to-spec/fan-out.js +77 -0
- package/dist/commands/codebase-to-spec/index.d.ts +9 -0
- package/dist/commands/codebase-to-spec/index.js +135 -0
- package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
- package/dist/commands/codebase-to-spec/pack.js +76 -0
- package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
- package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
- package/dist/commands/codebase-to-spec/present.d.ts +21 -0
- package/dist/commands/codebase-to-spec/present.js +92 -0
- package/dist/commands/codebase-to-spec/run.d.ts +20 -0
- package/dist/commands/codebase-to-spec/run.js +85 -0
- package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
- package/dist/commands/codebase-to-spec/skill-install.js +51 -0
- package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
- package/dist/commands/codebase-to-spec/specify-area.js +82 -0
- package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
- package/dist/commands/codebase-to-spec/style-check.js +42 -0
- package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
- package/dist/commands/codebase-to-spec/validate.js +38 -0
- package/dist/commands/create-requirement-document.d.ts +2 -0
- package/dist/commands/create-requirement-document.js +41 -0
- package/dist/commands/finalize.js +7 -7
- package/dist/commands/get.d.ts +2 -0
- package/dist/commands/get.js +55 -0
- package/dist/commands/init.js +132 -117
- package/dist/commands/link.js +27 -27
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.js +43 -0
- package/dist/commands/mcp-setup.js +159 -149
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/prepare.js +4 -4
- package/dist/commands/pull.js +116 -121
- package/dist/commands/push.js +106 -112
- package/dist/commands/report.d.ts +6 -2
- package/dist/commands/report.js +177 -122
- package/dist/commands/requirements-for.d.ts +2 -0
- package/dist/commands/requirements-for.js +29 -0
- package/dist/commands/review-test.d.ts +2 -0
- package/dist/commands/review-test.js +75 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.js +39 -0
- package/dist/commands/style-check.d.ts +7 -0
- package/dist/commands/style-check.js +75 -0
- package/dist/commands/test.js +53 -59
- package/dist/commands/tests-for.d.ts +2 -0
- package/dist/commands/tests-for.js +80 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.js +72 -0
- package/dist/config.js +1 -1
- package/dist/convex.d.ts +34 -22
- package/dist/convex.js +38 -22
- package/dist/harness/cache.d.ts +1 -5
- package/dist/harness/cache.js +49 -59
- package/dist/harness/convexReporting.d.ts +1 -1
- package/dist/harness/convexReporting.js +9 -7
- package/dist/harness/coverageCache.js +3 -3
- package/dist/harness/finalize.js +59 -46
- package/dist/harness/index.d.ts +6 -7
- package/dist/harness/index.js +9 -10
- package/dist/harness/prepare.js +6 -5
- package/dist/harness/requirementsLoader.d.ts +2 -2
- package/dist/harness/requirementsLoader.js +13 -35
- package/dist/harness/tracking.js +18 -18
- package/dist/harness/types.d.ts +1 -1
- package/dist/mcp/convexClient.d.ts +0 -39
- package/dist/mcp/convexClient.js +2 -107
- package/dist/mcp/grep.d.ts +1 -1
- package/dist/mcp/grep.js +87 -42
- package/dist/mcp/handlers/authoring.d.ts +1 -1
- package/dist/mcp/handlers/authoring.js +30 -234
- package/dist/mcp/handlers/coverage.d.ts +1 -1
- package/dist/mcp/handlers/coverage.js +13 -15
- package/dist/mcp/handlers/debug.d.ts +2 -3
- package/dist/mcp/handlers/debug.js +10 -10
- package/dist/mcp/handlers/get.d.ts +1 -1
- package/dist/mcp/handlers/get.js +11 -10
- package/dist/mcp/handlers/index.d.ts +20 -20
- package/dist/mcp/handlers/index.js +10 -10
- package/dist/mcp/handlers/list.d.ts +4 -33
- package/dist/mcp/handlers/list.js +16 -38
- package/dist/mcp/handlers/push.d.ts +1 -1
- package/dist/mcp/handlers/push.js +28 -18
- package/dist/mcp/handlers/report.d.ts +16 -0
- package/dist/mcp/handlers/report.js +134 -0
- package/dist/mcp/handlers/review.d.ts +1 -1
- package/dist/mcp/handlers/review.js +40 -59
- package/dist/mcp/handlers/search.d.ts +1 -1
- package/dist/mcp/handlers/search.js +7 -9
- package/dist/mcp/handlers/test-mapping.d.ts +1 -1
- package/dist/mcp/handlers/test-mapping.js +14 -14
- package/dist/mcp/handlers/types.d.ts +3 -3
- package/dist/mcp/handlers/types.js +2 -2
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +147 -167
- package/dist/mcp/requirements.d.ts +2 -2
- package/dist/mcp/requirements.js +30 -30
- package/dist/mcp/testCodeExtractor.js +24 -26
- package/dist/mcp/types.d.ts +1 -1
- package/dist/push/core.d.ts +2 -2
- package/dist/push/core.js +20 -20
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.js +2 -2
- package/dist/requirements/cloud-ai.d.ts +57 -0
- package/dist/requirements/cloud-ai.js +104 -0
- package/dist/requirements/cloud-coverage.d.ts +41 -0
- package/dist/requirements/cloud-coverage.js +60 -0
- package/dist/requirements/coverage.d.ts +45 -0
- package/dist/requirements/coverage.js +114 -0
- package/dist/requirements/grep.d.ts +33 -0
- package/dist/requirements/grep.js +306 -0
- package/dist/requirements/index.d.ts +73 -0
- package/dist/requirements/index.js +174 -0
- package/dist/requirements/style-guide.d.ts +67 -0
- package/dist/requirements/style-guide.js +299 -0
- package/dist/requirements/testCodeExtractor.d.ts +22 -0
- package/dist/requirements/testCodeExtractor.js +150 -0
- package/dist/schema/browser.d.ts +8 -8
- package/dist/schema/browser.js +13 -15
- package/dist/schema/builder.d.ts +1 -1
- package/dist/schema/builder.js +13 -44
- package/dist/schema/conversions.d.ts +2 -2
- package/dist/schema/conversions.js +11 -11
- package/dist/schema/index.d.ts +9 -9
- package/dist/schema/index.js +15 -15
- package/dist/schema/parser-core.d.ts +1 -1
- package/dist/schema/parser-core.js +23 -22
- package/dist/schema/parser.d.ts +3 -3
- package/dist/schema/parser.js +27 -31
- package/dist/schema/resolver.d.ts +1 -1
- package/dist/schema/resolver.js +9 -9
- package/dist/schema/scenario.d.ts +1 -1
- package/dist/schema/scenario.js +1 -1
- package/dist/schema/schemas.d.ts +3 -3
- package/dist/schema/schemas.js +41 -28
- package/dist/schema/test-schema.js +27 -27
- package/dist/templates/context-file-section.md +3 -2
- package/dist/templates/example-requirements.js +1 -1
- package/dist/templates/example-requirements.ts +3 -1
- package/dist/templates/requirements-readme.js +1 -1
- package/dist/templates/requirements-readme.ts +1 -1
- package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
- package/dist/utils/brand.js +3 -3
- package/dist/utils/browser-launch.js +4 -4
- package/dist/utils/context-file.d.ts +1 -1
- package/dist/utils/context-file.js +26 -26
- package/dist/utils/env.js +7 -7
- package/dist/utils/gitignore.js +7 -7
- package/dist/utils/oauth-callback-server.d.ts +1 -1
- package/dist/utils/oauth-callback-server.js +27 -25
- package/dist/utils/oauth-flow.js +32 -29
- package/dist/utils/project-discovery.d.ts +3 -3
- package/dist/utils/project-discovery.js +18 -17
- package/dist/utils/project-name.js +8 -8
- package/dist/utils/project-selector.d.ts +1 -1
- package/dist/utils/project-selector.js +24 -21
- package/dist/utils/project-settings.d.ts +1 -1
- package/dist/utils/project-settings.js +24 -22
- package/dist/utils/templates.js +6 -6
- package/package.json +2 -1
package/dist/commands/pull.js
CHANGED
|
@@ -1,130 +1,125 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import { ConvexHttpClient } from
|
|
4
|
-
import { getConvexUrl } from
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { getProjectCredentials } from
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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,
|
|
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):
|
package/dist/commands/push.js
CHANGED
|
@@ -1,120 +1,114 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import * as readline from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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 (
|
|
109
|
-
console.log(`
|
|
96
|
+
else if (isCreate) {
|
|
97
|
+
console.log(` ✓ Created: ${fileName} → ${file.metadata.document?.id}`);
|
|
110
98
|
}
|
|
111
|
-
|
|
112
|
-
console.log(
|
|
99
|
+
else {
|
|
100
|
+
console.log(` ✓ Updated: ${fileName}`);
|
|
113
101
|
}
|
|
114
102
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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(
|
|
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 ?
|
|
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() ===
|
|
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
|
-
|
|
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
|