@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/init.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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 { generateStyleGuideBody } from "../requirements/style-guide.js";
|
|
7
|
+
import { generateExampleRequirements } from "../templates/example-requirements.js";
|
|
8
|
+
import { generateRequirementsReadme } from "../templates/requirements-readme.js";
|
|
9
|
+
import { brand } from "../utils/brand.js";
|
|
10
|
+
import { ensureGitignore } from "../utils/gitignore.js";
|
|
11
|
+
import { executeOAuthFlow } from "../utils/oauth-flow.js";
|
|
12
|
+
import { detectProjectName } from "../utils/project-name.js";
|
|
13
|
+
import { getOrCreateProjectSecret, promptExpiryDays, selectProject, } from "../utils/project-selector.js";
|
|
14
|
+
import { findProjectRoot, writeProjectSettings, } from "../utils/project-settings.js";
|
|
15
|
+
import { promptChoice, promptConfirm } from "../utils/prompts.js";
|
|
16
|
+
import { aiSetupCommand } from "./ai-setup.js";
|
|
17
|
+
import { pullCommand } from "./pull.js";
|
|
17
18
|
export async function initCommand(options = {}) {
|
|
18
19
|
try {
|
|
19
20
|
console.log(`Initializing ${brand} project...\n`);
|
|
@@ -21,7 +22,7 @@ export async function initCommand(options = {}) {
|
|
|
21
22
|
// INIT-2: Check if .requirements/ folder already exists
|
|
22
23
|
const existingProject = findProjectRoot(cwd);
|
|
23
24
|
if (existingProject === cwd) {
|
|
24
|
-
console.log(
|
|
25
|
+
console.log("This project is already initialized.");
|
|
25
26
|
console.log('Use "dotrequirements link" to connect or reconnect to cloud.\n');
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
@@ -29,27 +30,27 @@ export async function initCommand(options = {}) {
|
|
|
29
30
|
if (options.invite) {
|
|
30
31
|
const initSucceeded = await inviteFlow(cwd, options.invite);
|
|
31
32
|
if (initSucceeded) {
|
|
32
|
-
console.log(
|
|
33
|
-
console.log(
|
|
34
|
-
const setupMcp = await promptConfirm(
|
|
33
|
+
console.log("\nDocumentation: https://docs.dotrequirements.io/getting-started");
|
|
34
|
+
console.log("By using this service, you agree to our Terms: https://app.dotrequirements.io/terms");
|
|
35
|
+
const setupMcp = await promptConfirm("\nConfigure AI assistant integration?", true);
|
|
35
36
|
if (setupMcp) {
|
|
36
|
-
await
|
|
37
|
+
await aiSetupCommand();
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
// INIT-1: Ask user whether to authenticate
|
|
42
|
-
const authChoice = await promptChoice(
|
|
43
|
-
{ title:
|
|
44
|
-
{ title:
|
|
43
|
+
const authChoice = await promptChoice("Do you want to authenticate before initializing a project? (Recommended)", [
|
|
44
|
+
{ title: "Yes", value: "yes" },
|
|
45
|
+
{ title: "No", value: "no" },
|
|
45
46
|
]);
|
|
46
47
|
if (!authChoice) {
|
|
47
48
|
// INIT-1.3: User cancelled
|
|
48
|
-
console.log(
|
|
49
|
+
console.log("Initialization cancelled.");
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
52
|
let initSucceeded;
|
|
52
|
-
if (authChoice ===
|
|
53
|
+
if (authChoice === "no") {
|
|
53
54
|
// INIT-3: Local-only workflow
|
|
54
55
|
initSucceeded = await localOnlyFlow(cwd);
|
|
55
56
|
}
|
|
@@ -57,22 +58,22 @@ export async function initCommand(options = {}) {
|
|
|
57
58
|
// INIT-17+: Authenticated workflow
|
|
58
59
|
initSucceeded = await authenticatedFlow(cwd, options);
|
|
59
60
|
}
|
|
60
|
-
console.log(
|
|
61
|
-
console.log(
|
|
61
|
+
console.log("\nDocumentation: https://docs.dotrequirements.io/getting-started");
|
|
62
|
+
console.log("By using this service, you agree to our Terms: https://app.dotrequirements.io/terms");
|
|
62
63
|
// Offer to configure AI assistant integration after successful init
|
|
63
64
|
if (initSucceeded) {
|
|
64
|
-
const setupMcp = await promptConfirm(
|
|
65
|
+
const setupMcp = await promptConfirm("\nConfigure AI assistant integration?", true);
|
|
65
66
|
if (setupMcp) {
|
|
66
|
-
await
|
|
67
|
+
await aiSetupCommand();
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
catch (error) {
|
|
71
72
|
console.error(`\n✗ Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
72
73
|
// INIT-11: Network error guidance
|
|
73
|
-
if (error instanceof Error && error.message.includes(
|
|
74
|
-
console.error(
|
|
75
|
-
console.error(
|
|
74
|
+
if (error instanceof Error && error.message.includes("fetch")) {
|
|
75
|
+
console.error("\nCheck your internet connection and try again.");
|
|
76
|
+
console.error("If the problem persists, visit https://status.convex.dev");
|
|
76
77
|
}
|
|
77
78
|
process.exit(1);
|
|
78
79
|
}
|
|
@@ -82,30 +83,31 @@ export async function initCommand(options = {}) {
|
|
|
82
83
|
* Returns true if project was created successfully
|
|
83
84
|
*/
|
|
84
85
|
async function localOnlyFlow(cwd) {
|
|
85
|
-
console.log(
|
|
86
|
+
console.log("\nInitializing local project...");
|
|
86
87
|
// INIT-3.0: Create .requirements/ directory
|
|
87
|
-
const requirementsDir = path.join(cwd,
|
|
88
|
+
const requirementsDir = path.join(cwd, ".requirements");
|
|
88
89
|
if (!fs.existsSync(requirementsDir)) {
|
|
89
90
|
fs.mkdirSync(requirementsDir, { recursive: true });
|
|
90
|
-
console.log(
|
|
91
|
+
console.log("✓ Created .requirements/ directory");
|
|
91
92
|
}
|
|
92
93
|
// Create README and example files
|
|
93
|
-
const readmePath = path.join(requirementsDir,
|
|
94
|
+
const readmePath = path.join(requirementsDir, "README.md");
|
|
94
95
|
fs.writeFileSync(readmePath, generateRequirementsReadme());
|
|
95
|
-
console.log(
|
|
96
|
-
const examplePath = path.join(requirementsDir,
|
|
96
|
+
console.log("✓ Created .requirements/README.md");
|
|
97
|
+
const examplePath = path.join(requirementsDir, "example.requirements.md");
|
|
97
98
|
if (!fs.existsSync(examplePath)) {
|
|
98
|
-
fs.writeFileSync(examplePath, generateExampleRequirements(
|
|
99
|
-
console.log(
|
|
99
|
+
fs.writeFileSync(examplePath, generateExampleRequirements("local"));
|
|
100
|
+
console.log("✓ Created .requirements/example.requirements.md");
|
|
100
101
|
}
|
|
102
|
+
scaffoldStyleMd(requirementsDir);
|
|
101
103
|
// INIT-5.2: Update .gitignore
|
|
102
104
|
ensureGitignore(cwd);
|
|
103
|
-
console.log(
|
|
105
|
+
console.log("✓ Updated .gitignore\n");
|
|
104
106
|
// INIT-3.1: No credentials written (local-only)
|
|
105
107
|
// INIT-3.2, INIT-3.3: Show guidance
|
|
106
|
-
console.log(
|
|
107
|
-
console.log(
|
|
108
|
-
console.log(
|
|
108
|
+
console.log("Next steps:");
|
|
109
|
+
console.log(" 1. Create requirements in .requirements/ directory");
|
|
110
|
+
console.log(" 2. Add the test harness to your test setup (see README)");
|
|
109
111
|
console.log(' 3. Run "dotrequirements link" to connect to cloud features');
|
|
110
112
|
return true;
|
|
111
113
|
}
|
|
@@ -123,8 +125,8 @@ async function authenticatedFlow(cwd, options) {
|
|
|
123
125
|
}
|
|
124
126
|
catch (error) {
|
|
125
127
|
// INIT-17.3: OAuth failed
|
|
126
|
-
console.error(
|
|
127
|
-
console.log(
|
|
128
|
+
console.error("\nAuthentication failed:", error instanceof Error ? error.message : String(error));
|
|
129
|
+
console.log("Please try again later.\n");
|
|
128
130
|
return false;
|
|
129
131
|
}
|
|
130
132
|
// Set up authenticated client using the session token
|
|
@@ -134,16 +136,16 @@ async function authenticatedFlow(cwd, options) {
|
|
|
134
136
|
const selectedTeam = await selectTeam(client);
|
|
135
137
|
if (selectedTeam === undefined) {
|
|
136
138
|
// User cancelled
|
|
137
|
-
console.log(
|
|
139
|
+
console.log("Initialization cancelled.");
|
|
138
140
|
return false;
|
|
139
141
|
}
|
|
140
142
|
// INIT-7: Choose between create and connect
|
|
141
143
|
const projectChoice = await chooseProjectAction(client, selectedTeam.teamId);
|
|
142
144
|
if (!projectChoice) {
|
|
143
|
-
console.log(
|
|
145
|
+
console.log("Initialization cancelled.");
|
|
144
146
|
return false;
|
|
145
147
|
}
|
|
146
|
-
if (projectChoice ===
|
|
148
|
+
if (projectChoice === "create") {
|
|
147
149
|
await createNewProjectFlow(cwd, client, selectedTeam, options);
|
|
148
150
|
}
|
|
149
151
|
else {
|
|
@@ -158,24 +160,24 @@ async function authenticatedFlow(cwd, options) {
|
|
|
158
160
|
* - undefined if user cancelled
|
|
159
161
|
*/
|
|
160
162
|
async function selectTeam(client) {
|
|
161
|
-
console.log(
|
|
163
|
+
console.log("Fetching your teams...\n");
|
|
162
164
|
// INIT-18.0: Ensure user has a default team if they don't have any
|
|
163
165
|
await client.mutation(api.teams.mutations.ensureDefaultTeam, {});
|
|
164
166
|
// Query all teams for the authenticated user
|
|
165
|
-
const teams = await client.query(api.teams.queries.listForUserWithUsage);
|
|
167
|
+
const teams = (await client.query(api.teams.queries.listForUserWithUsage));
|
|
166
168
|
if (teams.length === 0) {
|
|
167
|
-
console.log(
|
|
168
|
-
console.log(
|
|
169
|
+
console.log("\nYou are not a member of any teams.");
|
|
170
|
+
console.log("To create a new team, visit: https://app.dotrequirements.io/teams/new");
|
|
169
171
|
console.log('After creating your team, run "dotrequirements init" again.\n');
|
|
170
172
|
process.exit(0);
|
|
171
173
|
}
|
|
172
174
|
// INIT-18.1, INIT-18.2, INIT-18.3: Build team selection options with usage display
|
|
173
175
|
const teamChoices = teams.map((team) => {
|
|
174
176
|
const limitDisplay = team.projectLimit === -1
|
|
175
|
-
?
|
|
177
|
+
? "unlimited"
|
|
176
178
|
: `${team.projectCount}/${team.projectLimit}`;
|
|
177
179
|
// INIT-18.3: Mark teams at capacity
|
|
178
|
-
const capacityMarker = team.isAtCapacity ?
|
|
180
|
+
const capacityMarker = team.isAtCapacity ? " [at capacity]" : "";
|
|
179
181
|
return {
|
|
180
182
|
title: `${team.name} (${team.tierName} - ${limitDisplay} projects)${capacityMarker}`,
|
|
181
183
|
value: team.teamId,
|
|
@@ -185,23 +187,23 @@ async function selectTeam(client) {
|
|
|
185
187
|
// INIT-18.4: Add "Create new team" option if user can create one
|
|
186
188
|
// (Check if user is already admin of a free team - simplified to always show for now)
|
|
187
189
|
teamChoices.push({
|
|
188
|
-
title:
|
|
189
|
-
value:
|
|
190
|
+
title: "Create new team",
|
|
191
|
+
value: "CREATE_NEW_TEAM",
|
|
190
192
|
disabled: false,
|
|
191
193
|
});
|
|
192
|
-
const selectedTeamId = await promptChoice(
|
|
194
|
+
const selectedTeamId = await promptChoice("Select a team for this project:", teamChoices);
|
|
193
195
|
if (!selectedTeamId) {
|
|
194
196
|
return undefined;
|
|
195
197
|
}
|
|
196
198
|
// INIT-14: Handle selection of at-capacity team (defensive check)
|
|
197
199
|
const selectedTeam = teams.find((t) => t.teamId === selectedTeamId);
|
|
198
|
-
if (selectedTeam
|
|
199
|
-
console.error(
|
|
200
|
+
if (selectedTeam?.isAtCapacity) {
|
|
201
|
+
console.error("\n✗ This team is at its project limit. Please select a different team or create a new one.\n");
|
|
200
202
|
return await selectTeam(client);
|
|
201
203
|
}
|
|
202
204
|
// INIT-15: Handle "Create new team" selection
|
|
203
|
-
if (selectedTeamId ===
|
|
204
|
-
console.log(
|
|
205
|
+
if (selectedTeamId === "CREATE_NEW_TEAM") {
|
|
206
|
+
console.log("\nTo create a new team, visit: https://app.dotrequirements.io/teams/new");
|
|
205
207
|
console.log('After creating your team, run "dotrequirements init" again.\n');
|
|
206
208
|
process.exit(0);
|
|
207
209
|
}
|
|
@@ -215,16 +217,18 @@ async function selectTeam(client) {
|
|
|
215
217
|
*/
|
|
216
218
|
async function chooseProjectAction(client, teamId) {
|
|
217
219
|
// INIT-7.3: Check if team has existing projects the user can access
|
|
218
|
-
const projectsWithSecrets = await client.query(api.projectSecrets.queries.listOwnSecretsForTeam,
|
|
220
|
+
const projectsWithSecrets = (await client.query(api.projectSecrets.queries.listOwnSecretsForTeam,
|
|
221
|
+
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id<"teams"> type; the runtime value is a plain string the convex server validates.
|
|
222
|
+
{ target: { type: "team", id: teamId } }));
|
|
219
223
|
if (projectsWithSecrets.length === 0) {
|
|
220
224
|
// No existing projects, only offer create
|
|
221
|
-
const confirmed = await promptConfirm(
|
|
222
|
-
return confirmed ?
|
|
225
|
+
const confirmed = await promptConfirm("This team has no projects. Create a new one?", true);
|
|
226
|
+
return confirmed ? "create" : undefined;
|
|
223
227
|
}
|
|
224
228
|
// INIT-7.0: Offer both options
|
|
225
|
-
return await promptChoice(
|
|
226
|
-
{ title:
|
|
227
|
-
{ title:
|
|
229
|
+
return await promptChoice("What would you like to do?", [
|
|
230
|
+
{ title: "Create a new project", value: "create" },
|
|
231
|
+
{ title: "Connect to an existing project", value: "connect" },
|
|
228
232
|
]);
|
|
229
233
|
}
|
|
230
234
|
/**
|
|
@@ -234,33 +238,33 @@ async function createNewProjectFlow(cwd, client, team, options) {
|
|
|
234
238
|
// INIT-4.0: Detect/prompt for project name
|
|
235
239
|
const projectName = await detectProjectName({ name: options.name });
|
|
236
240
|
console.log(`Project name: ${projectName}\n`);
|
|
237
|
-
console.log(
|
|
241
|
+
console.log("Creating cloud project...");
|
|
238
242
|
// INIT-4.1: Create project in cloud (no longer auto-creates secret)
|
|
239
|
-
const result = await client.mutation(api.projects.mutations.create, {
|
|
243
|
+
const result = (await client.mutation(api.projects.mutations.create, {
|
|
240
244
|
name: projectName,
|
|
241
245
|
teamId: team.teamId,
|
|
242
|
-
});
|
|
246
|
+
}));
|
|
243
247
|
const projectId = result.slug;
|
|
244
248
|
console.log(`✓ Cloud project created: ${projectId}`);
|
|
245
249
|
// AUTHZ-3: Prompt for secret expiry (paid tier only)
|
|
246
250
|
const expiryDays = await promptExpiryDays(team.tier);
|
|
247
251
|
// Get secret for the newly created project
|
|
248
|
-
console.log(
|
|
252
|
+
console.log("Getting project credentials...");
|
|
249
253
|
const secretResult = await getOrCreateProjectSecret(client, result.projectId, expiryDays);
|
|
250
254
|
const secret = secretResult.secret;
|
|
251
|
-
console.log(
|
|
255
|
+
console.log("✓ Created project secret");
|
|
252
256
|
// INIT-4.2: Write credentials to project-settings.json
|
|
253
257
|
writeProjectSettings(cwd, { projectId, projectSecret: secret });
|
|
254
|
-
console.log(
|
|
258
|
+
console.log("✓ Wrote credentials to .requirements/project-settings.json");
|
|
255
259
|
// INIT-5.0, INIT-5.1: Create directory structure (if not already created by writeProjectSettings)
|
|
256
260
|
await createRequirementsDirectory(cwd, projectId);
|
|
257
261
|
// INIT-5.2: Update .gitignore
|
|
258
262
|
ensureGitignore(cwd);
|
|
259
|
-
console.log(
|
|
263
|
+
console.log("✓ Updated .gitignore\n");
|
|
260
264
|
// INIT-4.3: Show guidance
|
|
261
|
-
console.log(
|
|
262
|
-
console.log(
|
|
263
|
-
console.log(
|
|
265
|
+
console.log("Next steps:");
|
|
266
|
+
console.log(" 1. Create requirements in .requirements/ directory");
|
|
267
|
+
console.log(" 2. Add the test harness to your test setup (see README)");
|
|
264
268
|
}
|
|
265
269
|
/**
|
|
266
270
|
* INIT-6, INIT-16: Connect to an existing project
|
|
@@ -269,39 +273,39 @@ async function connectExistingProjectFlow(cwd, client, team) {
|
|
|
269
273
|
// INIT-6.0, INIT-16.0: Select project from team
|
|
270
274
|
const selectedProject = await selectProject(client, team.teamId);
|
|
271
275
|
if (!selectedProject) {
|
|
272
|
-
console.log(
|
|
276
|
+
console.log("Initialization cancelled.");
|
|
273
277
|
return;
|
|
274
278
|
}
|
|
275
279
|
// AUTHZ-3: Prompt for secret expiry (paid tier only, if creating new secret)
|
|
276
280
|
const expiryDays = await promptExpiryDays(team.tier);
|
|
277
281
|
// INIT-16.1: Get or create secret for this project
|
|
278
|
-
console.log(
|
|
282
|
+
console.log("\nGetting project credentials...");
|
|
279
283
|
const secretResult = await getOrCreateProjectSecret(client, selectedProject.projectId, expiryDays);
|
|
280
284
|
const projectId = selectedProject.projectSlug || selectedProject.projectId;
|
|
281
285
|
const secret = secretResult.secret;
|
|
282
286
|
if (secretResult.created) {
|
|
283
|
-
console.log(
|
|
287
|
+
console.log("✓ Created new project secret");
|
|
284
288
|
}
|
|
285
289
|
else {
|
|
286
|
-
console.log(
|
|
290
|
+
console.log("✓ Retrieved existing project secret");
|
|
287
291
|
}
|
|
288
292
|
// INIT-6.1: Write credentials to project-settings.json
|
|
289
293
|
writeProjectSettings(cwd, { projectId, projectSecret: secret });
|
|
290
|
-
console.log(
|
|
294
|
+
console.log("✓ Wrote credentials to .requirements/project-settings.json");
|
|
291
295
|
// Create directory structure
|
|
292
296
|
await createRequirementsDirectory(cwd, projectId);
|
|
293
297
|
// Update .gitignore
|
|
294
298
|
ensureGitignore(cwd);
|
|
295
|
-
console.log(
|
|
299
|
+
console.log("✓ Updated .gitignore\n");
|
|
296
300
|
// INIT-6.2: Pull existing requirements from cloud
|
|
297
|
-
console.log(
|
|
301
|
+
console.log("Pulling requirements from cloud...");
|
|
298
302
|
await pullCommand({ project: projectId });
|
|
299
303
|
console.log(`\n✓ Successfully connected to project: ${selectedProject.projectName}`);
|
|
300
304
|
console.log(` Project ID: ${projectId}`);
|
|
301
305
|
// INIT-6.3: Show guidance
|
|
302
|
-
console.log(
|
|
303
|
-
console.log(
|
|
304
|
-
console.log(
|
|
306
|
+
console.log("\nNext steps:");
|
|
307
|
+
console.log(" 1. Review the requirements in .requirements/");
|
|
308
|
+
console.log(" 2. Add the test harness to your test setup (see README)");
|
|
305
309
|
}
|
|
306
310
|
/**
|
|
307
311
|
* MEMBERSHIP-18: Invite flow - join a team using an invite token
|
|
@@ -310,18 +314,20 @@ async function connectExistingProjectFlow(cwd, client, team) {
|
|
|
310
314
|
async function inviteFlow(cwd, token) {
|
|
311
315
|
// MEMBERSHIP-18.0: Validate token before auth (no auth needed for this query)
|
|
312
316
|
const client = new ConvexHttpClient(getConvexUrl());
|
|
313
|
-
console.log(
|
|
314
|
-
const inviteInfo = await client.query(api.teamInvites.queries.getInviteInfo, {
|
|
317
|
+
console.log("Validating invite token...");
|
|
318
|
+
const inviteInfo = await client.query(api.teamInvites.queries.getInviteInfo, {
|
|
319
|
+
token,
|
|
320
|
+
});
|
|
315
321
|
// MEMBERSHIP-18.3: Handle invalid tokens
|
|
316
322
|
if (!inviteInfo.valid) {
|
|
317
323
|
const errorMessages = {
|
|
318
|
-
invalid:
|
|
319
|
-
expired:
|
|
320
|
-
already_used:
|
|
324
|
+
invalid: "This invite token is not valid.",
|
|
325
|
+
expired: "This invite token has expired.",
|
|
326
|
+
already_used: "This invite token has already been used.",
|
|
321
327
|
};
|
|
322
|
-
const message = errorMessages[inviteInfo.error] ||
|
|
328
|
+
const message = errorMessages[inviteInfo.error] || "Invalid invite token.";
|
|
323
329
|
console.error(`\n✗ ${message}`);
|
|
324
|
-
console.log(
|
|
330
|
+
console.log("Please ask the team admin for a new invite link.\n");
|
|
325
331
|
return false;
|
|
326
332
|
}
|
|
327
333
|
console.log(`\nYou've been invited to join team "${inviteInfo.teamName}"\n`);
|
|
@@ -332,8 +338,8 @@ async function inviteFlow(cwd, token) {
|
|
|
332
338
|
console.log();
|
|
333
339
|
}
|
|
334
340
|
catch (error) {
|
|
335
|
-
console.error(
|
|
336
|
-
console.log(
|
|
341
|
+
console.error("\nAuthentication failed:", error instanceof Error ? error.message : String(error));
|
|
342
|
+
console.log("Please try again later.\n");
|
|
337
343
|
return false;
|
|
338
344
|
}
|
|
339
345
|
// Set up authenticated client
|
|
@@ -348,7 +354,7 @@ async function inviteFlow(cwd, token) {
|
|
|
348
354
|
}
|
|
349
355
|
catch (err) {
|
|
350
356
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
351
|
-
if (errorMessage.includes(
|
|
357
|
+
if (errorMessage.includes("already a member")) {
|
|
352
358
|
console.log(`Already a member of "${inviteInfo.teamName}"`);
|
|
353
359
|
}
|
|
354
360
|
else {
|
|
@@ -358,65 +364,74 @@ async function inviteFlow(cwd, token) {
|
|
|
358
364
|
// MEMBERSHIP-18.1.0: Select project from team
|
|
359
365
|
const teamId = inviteInfo.teamId;
|
|
360
366
|
// Get team tier for secret expiry prompt
|
|
361
|
-
const teams = await client.query(api.teams.queries.listForUserWithUsage);
|
|
367
|
+
const teams = (await client.query(api.teams.queries.listForUserWithUsage));
|
|
362
368
|
const team = teams.find((t) => t.teamId === teamId);
|
|
363
|
-
const tier = team?.tier ||
|
|
369
|
+
const tier = team?.tier || "free";
|
|
364
370
|
const selectedProject = await selectProject(client, teamId);
|
|
365
371
|
if (!selectedProject) {
|
|
366
|
-
console.log(
|
|
372
|
+
console.log("Initialization cancelled.");
|
|
367
373
|
return false;
|
|
368
374
|
}
|
|
369
375
|
// AUTHZ-3: Prompt for secret expiry (paid tier only)
|
|
370
376
|
const expiryDays = await promptExpiryDays(tier);
|
|
371
377
|
// Get or create secret for this project
|
|
372
|
-
console.log(
|
|
378
|
+
console.log("\nGetting project credentials...");
|
|
373
379
|
const secretResult = await getOrCreateProjectSecret(client, selectedProject.projectId, expiryDays);
|
|
374
380
|
const projectId = selectedProject.projectSlug || selectedProject.projectId;
|
|
375
381
|
const secret = secretResult.secret;
|
|
376
382
|
if (secretResult.created) {
|
|
377
|
-
console.log(
|
|
383
|
+
console.log("✓ Created new project secret");
|
|
378
384
|
}
|
|
379
385
|
else {
|
|
380
|
-
console.log(
|
|
386
|
+
console.log("✓ Retrieved existing project secret");
|
|
381
387
|
}
|
|
382
388
|
// Write credentials to project-settings.json
|
|
383
389
|
writeProjectSettings(cwd, { projectId, projectSecret: secret });
|
|
384
|
-
console.log(
|
|
390
|
+
console.log("✓ Wrote credentials to .requirements/project-settings.json");
|
|
385
391
|
// Create directory structure
|
|
386
392
|
await createRequirementsDirectory(cwd, projectId);
|
|
387
393
|
// Update .gitignore
|
|
388
394
|
ensureGitignore(cwd);
|
|
389
|
-
console.log(
|
|
395
|
+
console.log("✓ Updated .gitignore\n");
|
|
390
396
|
// Pull existing requirements from cloud
|
|
391
|
-
console.log(
|
|
397
|
+
console.log("Pulling requirements from cloud...");
|
|
392
398
|
await pullCommand({ project: projectId });
|
|
393
399
|
console.log(`\n✓ Successfully connected to project: ${selectedProject.projectName}`);
|
|
394
400
|
console.log(` Project ID: ${projectId}`);
|
|
395
401
|
// Show guidance
|
|
396
|
-
console.log(
|
|
397
|
-
console.log(
|
|
398
|
-
console.log(
|
|
402
|
+
console.log("\nNext steps:");
|
|
403
|
+
console.log(" 1. Review the requirements in .requirements/");
|
|
404
|
+
console.log(" 2. Add the test harness to your test setup (see README)");
|
|
399
405
|
return true;
|
|
400
406
|
}
|
|
401
407
|
/**
|
|
402
408
|
* INIT-5: Create .requirements/ directory with README and example
|
|
403
409
|
*/
|
|
404
410
|
async function createRequirementsDirectory(cwd, projectId) {
|
|
405
|
-
const requirementsDir = path.join(cwd,
|
|
411
|
+
const requirementsDir = path.join(cwd, ".requirements");
|
|
406
412
|
// Directory may already exist from writeProjectSettings
|
|
407
413
|
if (!fs.existsSync(requirementsDir)) {
|
|
408
414
|
fs.mkdirSync(requirementsDir, { recursive: true });
|
|
409
|
-
console.log(
|
|
415
|
+
console.log("✓ Created .requirements/ directory");
|
|
410
416
|
}
|
|
411
417
|
// INIT-5.0: Always create/update README
|
|
412
|
-
const readmePath = path.join(requirementsDir,
|
|
418
|
+
const readmePath = path.join(requirementsDir, "README.md");
|
|
413
419
|
fs.writeFileSync(readmePath, generateRequirementsReadme());
|
|
414
|
-
console.log(
|
|
420
|
+
console.log("✓ Created .requirements/README.md");
|
|
415
421
|
// INIT-5.1: Create example.requirements.md
|
|
416
|
-
const examplePath = path.join(requirementsDir,
|
|
422
|
+
const examplePath = path.join(requirementsDir, "example.requirements.md");
|
|
417
423
|
if (!fs.existsSync(examplePath)) {
|
|
418
424
|
fs.writeFileSync(examplePath, generateExampleRequirements(projectId));
|
|
419
|
-
console.log(
|
|
425
|
+
console.log("✓ Created .requirements/example.requirements.md");
|
|
426
|
+
}
|
|
427
|
+
scaffoldStyleMd(requirementsDir);
|
|
428
|
+
}
|
|
429
|
+
function scaffoldStyleMd(requirementsDir) {
|
|
430
|
+
const stylePath = path.join(requirementsDir, "STYLE.md");
|
|
431
|
+
if (fs.existsSync(stylePath)) {
|
|
432
|
+
return;
|
|
420
433
|
}
|
|
434
|
+
fs.writeFileSync(stylePath, generateStyleGuideBody({ requirements: [], customStyleGuidance: null }));
|
|
435
|
+
console.log("✓ Created .requirements/STYLE.md");
|
|
421
436
|
}
|
|
422
437
|
//# sourceMappingURL=init.js.map
|
package/dist/commands/link.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { getConvexUrl } from "../config.js";
|
|
3
|
+
import { brand } from "../utils/brand.js";
|
|
4
|
+
import { ensureGitignore } from "../utils/gitignore.js";
|
|
5
|
+
import { executeOAuthFlow } from "../utils/oauth-flow.js";
|
|
6
|
+
import { getOrCreateProjectSecret, promptExpiryDays, selectProject, selectTeam, } from "../utils/project-selector.js";
|
|
7
|
+
import { findProjectRoot, readProjectSettings, writeProjectSettings, } from "../utils/project-settings.js";
|
|
8
|
+
import { promptChoice, promptConfirm } from "../utils/prompts.js";
|
|
9
|
+
import { pullCommand } from "./pull.js";
|
|
10
10
|
/**
|
|
11
11
|
* Link command - connect or reconnect local project to cloud.
|
|
12
12
|
*
|
|
@@ -26,7 +26,7 @@ export async function linkCommand() {
|
|
|
26
26
|
// LINK-1: Check if .requirements/ folder exists
|
|
27
27
|
const projectRoot = findProjectRoot(cwd);
|
|
28
28
|
if (!projectRoot || projectRoot !== cwd) {
|
|
29
|
-
console.log(
|
|
29
|
+
console.log("No .requirements/ folder found.");
|
|
30
30
|
console.log('Run "dotrequirements init" to initialize a project first.\n');
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
@@ -52,8 +52,8 @@ export async function linkCommand() {
|
|
|
52
52
|
console.log();
|
|
53
53
|
}
|
|
54
54
|
catch (error) {
|
|
55
|
-
console.error(
|
|
56
|
-
console.log(
|
|
55
|
+
console.error("\nAuthentication failed:", error instanceof Error ? error.message : String(error));
|
|
56
|
+
console.log("Please try again later.\n");
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
// Set up authenticated client using the session token
|
|
@@ -62,7 +62,7 @@ export async function linkCommand() {
|
|
|
62
62
|
// LINK-4: Select team
|
|
63
63
|
const selectedTeam = await selectTeam(client);
|
|
64
64
|
if (!selectedTeam) {
|
|
65
|
-
console.log(
|
|
65
|
+
console.log("Link cancelled.");
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
// LINK-5: Select project
|
|
@@ -74,37 +74,37 @@ export async function linkCommand() {
|
|
|
74
74
|
// AUTHZ-3: Prompt for secret expiry (paid tier only)
|
|
75
75
|
const expiryDays = await promptExpiryDays(selectedTeam.tier);
|
|
76
76
|
// LINK-5.3: Get or create secret for this project
|
|
77
|
-
console.log(
|
|
77
|
+
console.log("\nGetting project credentials...");
|
|
78
78
|
const secretResult = await getOrCreateProjectSecret(client, selectedProject.projectId, expiryDays);
|
|
79
79
|
const projectId = selectedProject.projectSlug || selectedProject.projectId;
|
|
80
80
|
const secret = secretResult.secret;
|
|
81
81
|
if (secretResult.created) {
|
|
82
|
-
console.log(
|
|
82
|
+
console.log("✓ Created new project secret");
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
console.log(
|
|
85
|
+
console.log("✓ Retrieved existing project secret");
|
|
86
86
|
}
|
|
87
87
|
// LINK-6: Write credentials to project-settings.json
|
|
88
88
|
writeProjectSettings(projectRoot, { projectId, projectSecret: secret });
|
|
89
89
|
ensureGitignore(projectRoot);
|
|
90
|
-
console.log(
|
|
90
|
+
console.log("✓ Wrote credentials to .requirements/project-settings.json");
|
|
91
91
|
// LINK-7: Optionally pull requirements
|
|
92
|
-
const shouldPull = await promptConfirm(
|
|
92
|
+
const shouldPull = await promptConfirm("Pull requirements from cloud now?", true);
|
|
93
93
|
if (shouldPull) {
|
|
94
|
-
console.log(
|
|
94
|
+
console.log("\nPulling requirements...");
|
|
95
95
|
await pullCommand({ project: projectId });
|
|
96
96
|
}
|
|
97
97
|
// LINK-7.2: Success message
|
|
98
98
|
console.log(`\n✓ Successfully linked to project: ${selectedProject.projectName}`);
|
|
99
99
|
console.log(` Project ID: ${projectId}`);
|
|
100
|
-
console.log(
|
|
100
|
+
console.log("\nDocumentation: https://docs.dotrequirements.io/getting-started");
|
|
101
101
|
}
|
|
102
102
|
catch (error) {
|
|
103
103
|
console.error(`\n✗ Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
104
104
|
// LINK-8: Network error guidance
|
|
105
|
-
if (error instanceof Error && error.message.includes(
|
|
106
|
-
console.error(
|
|
107
|
-
console.error(
|
|
105
|
+
if (error instanceof Error && error.message.includes("fetch")) {
|
|
106
|
+
console.error("\nCheck your internet connection and try again.");
|
|
107
|
+
console.error("If the problem persists, visit https://status.convex.dev");
|
|
108
108
|
}
|
|
109
109
|
process.exit(1);
|
|
110
110
|
}
|
|
@@ -116,12 +116,12 @@ export async function linkCommand() {
|
|
|
116
116
|
*/
|
|
117
117
|
async function handleExistingCredentials(credentials) {
|
|
118
118
|
console.log(`Currently linked to: ${credentials.projectId}\n`);
|
|
119
|
-
const choice = await promptChoice(
|
|
120
|
-
{ title:
|
|
121
|
-
{ title:
|
|
119
|
+
const choice = await promptChoice("What would you like to do?", [
|
|
120
|
+
{ title: "Refresh credentials for this project", value: "refresh" },
|
|
121
|
+
{ title: "Connect to a different project", value: "different" },
|
|
122
122
|
]);
|
|
123
123
|
if (!choice) {
|
|
124
|
-
console.log(
|
|
124
|
+
console.log("Link cancelled.");
|
|
125
125
|
return false;
|
|
126
126
|
}
|
|
127
127
|
// Both options continue with OAuth flow
|