@popoverai/dotrequirements 0.18.0 → 0.19.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 +18 -0
- package/dist/cli.js +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +108 -0
- package/dist/convex.d.ts +8 -0
- package/dist/convex.js +8 -0
- package/dist/templates/context-file-section.md +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,8 +96,26 @@ Initialize a new project. Creates `.requirements/` directory, example files, and
|
|
|
96
96
|
```bash
|
|
97
97
|
dotreq init
|
|
98
98
|
dotreq init --name my-project
|
|
99
|
+
dotreq init --invite abc123xyz
|
|
99
100
|
```
|
|
100
101
|
|
|
102
|
+
**Options:**
|
|
103
|
+
|
|
104
|
+
| Option | Description |
|
|
105
|
+
|--------|-------------|
|
|
106
|
+
| `--name <name>` | Set project name (skips prompt) |
|
|
107
|
+
| `--invite <token>` | Join a team using an invite token |
|
|
108
|
+
|
|
109
|
+
**Joining a team via invite:**
|
|
110
|
+
|
|
111
|
+
If a team admin shares an invite command with you, join the team and connect in one step:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx dotrequirements init --invite abc123xyz
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This shows the team name, prompts you to sign in, adds you to the team, and lets you select a project.
|
|
118
|
+
|
|
101
119
|
### `dotreq link`
|
|
102
120
|
|
|
103
121
|
Link your local environment to an existing project (when you already have a project in dot•requirements cloud).
|
package/dist/cli.js
CHANGED
|
@@ -47,6 +47,7 @@ program
|
|
|
47
47
|
.command('init')
|
|
48
48
|
.description('Initialize a new dotrequirements project')
|
|
49
49
|
.option('-n, --name <name>', 'Project name (defaults to package.json name or directory name)')
|
|
50
|
+
.option('-i, --invite <token>', 'Join a team using an invite token')
|
|
50
51
|
.action(wrapCommand(initCommand));
|
|
51
52
|
program
|
|
52
53
|
.command('link')
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -25,6 +25,19 @@ export async function initCommand(options = {}) {
|
|
|
25
25
|
console.log('Use "dotrequirements link" to connect or reconnect to cloud.\n');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
+
// MEMBERSHIP-18: If invite token provided, use invite flow
|
|
29
|
+
if (options.invite) {
|
|
30
|
+
const initSucceeded = await inviteFlow(cwd, options.invite);
|
|
31
|
+
if (initSucceeded) {
|
|
32
|
+
console.log('\nDocumentation: https://docs.dotrequirements.io/getting-started');
|
|
33
|
+
console.log('By using this service, you agree to our Terms: https://app.dotrequirements.io/terms');
|
|
34
|
+
const setupMcp = await promptConfirm('\nConfigure AI assistant integration?', true);
|
|
35
|
+
if (setupMcp) {
|
|
36
|
+
await mcpSetupCommand();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
28
41
|
// INIT-1: Ask user whether to authenticate
|
|
29
42
|
const authChoice = await promptChoice('Do you want to authenticate before initializing a project? (Recommended)', [
|
|
30
43
|
{ title: 'Yes', value: 'yes' },
|
|
@@ -290,6 +303,101 @@ async function connectExistingProjectFlow(cwd, client, team) {
|
|
|
290
303
|
console.log(' 1. Review the requirements in .requirements/');
|
|
291
304
|
console.log(' 2. Add the test harness to your test setup (see README)');
|
|
292
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* MEMBERSHIP-18: Invite flow - join a team using an invite token
|
|
308
|
+
* Returns true if successfully joined and connected
|
|
309
|
+
*/
|
|
310
|
+
async function inviteFlow(cwd, token) {
|
|
311
|
+
// MEMBERSHIP-18.0: Validate token before auth (no auth needed for this query)
|
|
312
|
+
const client = new ConvexHttpClient(getConvexUrl());
|
|
313
|
+
console.log('Validating invite token...');
|
|
314
|
+
const inviteInfo = await client.query(api.teamInvites.queries.getInviteInfo, { token });
|
|
315
|
+
// MEMBERSHIP-18.3: Handle invalid tokens
|
|
316
|
+
if (!inviteInfo.valid) {
|
|
317
|
+
const errorMessages = {
|
|
318
|
+
invalid: 'This invite token is not valid.',
|
|
319
|
+
expired: 'This invite token has expired.',
|
|
320
|
+
already_used: 'This invite token has already been used.',
|
|
321
|
+
};
|
|
322
|
+
const message = errorMessages[inviteInfo.error] || 'Invalid invite token.';
|
|
323
|
+
console.error(`\n✗ ${message}`);
|
|
324
|
+
console.log('Please ask the team admin for a new invite link.\n');
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
console.log(`\nYou've been invited to join team "${inviteInfo.teamName}"\n`);
|
|
328
|
+
// MEMBERSHIP-18.1: Auth flow
|
|
329
|
+
let oauthResult;
|
|
330
|
+
try {
|
|
331
|
+
oauthResult = await executeOAuthFlow();
|
|
332
|
+
console.log();
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
console.error('\nAuthentication failed:', error instanceof Error ? error.message : String(error));
|
|
336
|
+
console.log('Please try again later.\n');
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
// Set up authenticated client
|
|
340
|
+
client.setAuth(oauthResult.accessToken);
|
|
341
|
+
// Ensure default team (this also claims any pending email invites via MEMBERSHIP-4)
|
|
342
|
+
await client.mutation(api.teams.mutations.ensureDefaultTeam, {});
|
|
343
|
+
// MEMBERSHIP-18.1: Accept invite (joins team, marks token used)
|
|
344
|
+
// MEMBERSHIP-18.2: If already a member, continue gracefully
|
|
345
|
+
try {
|
|
346
|
+
await client.action(api.polar.acceptTeamInvite, { token });
|
|
347
|
+
console.log(`✓ Joined team "${inviteInfo.teamName}"`);
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
351
|
+
if (errorMessage.includes('already a member')) {
|
|
352
|
+
console.log(`Already a member of "${inviteInfo.teamName}"`);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
throw err;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// MEMBERSHIP-18.1.0: Select project from team
|
|
359
|
+
const teamId = inviteInfo.teamId;
|
|
360
|
+
// Get team tier for secret expiry prompt
|
|
361
|
+
const teams = await client.query(api.teams.queries.listForUserWithUsage);
|
|
362
|
+
const team = teams.find((t) => t.teamId === teamId);
|
|
363
|
+
const tier = team?.tier || 'free';
|
|
364
|
+
const selectedProject = await selectProject(client, teamId);
|
|
365
|
+
if (!selectedProject) {
|
|
366
|
+
console.log('Initialization cancelled.');
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
// AUTHZ-3: Prompt for secret expiry (paid tier only)
|
|
370
|
+
const expiryDays = await promptExpiryDays(tier);
|
|
371
|
+
// Get or create secret for this project
|
|
372
|
+
console.log('\nGetting project credentials...');
|
|
373
|
+
const secretResult = await getOrCreateProjectSecret(client, selectedProject.projectId, expiryDays);
|
|
374
|
+
const projectId = selectedProject.projectSlug || selectedProject.projectId;
|
|
375
|
+
const secret = secretResult.secret;
|
|
376
|
+
if (secretResult.created) {
|
|
377
|
+
console.log('✓ Created new project secret');
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
console.log('✓ Retrieved existing project secret');
|
|
381
|
+
}
|
|
382
|
+
// Write credentials to project-settings.json
|
|
383
|
+
writeProjectSettings(cwd, { projectId, projectSecret: secret });
|
|
384
|
+
console.log('✓ Wrote credentials to .requirements/project-settings.json');
|
|
385
|
+
// Create directory structure
|
|
386
|
+
await createRequirementsDirectory(cwd, projectId);
|
|
387
|
+
// Update .gitignore
|
|
388
|
+
ensureGitignore(cwd);
|
|
389
|
+
console.log('✓ Updated .gitignore\n');
|
|
390
|
+
// Pull existing requirements from cloud
|
|
391
|
+
console.log('Pulling requirements from cloud...');
|
|
392
|
+
await pullCommand({ project: projectId });
|
|
393
|
+
console.log(`\n✓ Successfully connected to project: ${selectedProject.projectName}`);
|
|
394
|
+
console.log(` Project ID: ${projectId}`);
|
|
395
|
+
// Show guidance
|
|
396
|
+
console.log('\nNext steps:');
|
|
397
|
+
console.log(' 1. Review the requirements in .requirements/');
|
|
398
|
+
console.log(' 2. Add the test harness to your test setup (see README)');
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
293
401
|
/**
|
|
294
402
|
* INIT-5: Create .requirements/ directory with README and example
|
|
295
403
|
*/
|
package/dist/convex.d.ts
CHANGED
package/dist/convex.js
CHANGED
|
@@ -41,6 +41,14 @@ export const api = {
|
|
|
41
41
|
ensureDefaultTeam: 'teams/mutations:ensureDefaultTeam',
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
|
+
teamInvites: {
|
|
45
|
+
queries: {
|
|
46
|
+
getInviteInfo: 'teamInvites/queries:getInviteInfo',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
polar: {
|
|
50
|
+
acceptTeamInvite: 'polar:acceptTeamInvite',
|
|
51
|
+
},
|
|
44
52
|
projectSecrets: {
|
|
45
53
|
queries: {
|
|
46
54
|
getOwnSecret: 'projectSecrets/queries:getOwnSecret',
|
|
@@ -4,6 +4,10 @@ This project uses dotrequirements for requirements tracking. Requirements live i
|
|
|
4
4
|
|
|
5
5
|
### Workflow
|
|
6
6
|
|
|
7
|
+
**Plan mode:** When planning behavioral changes, your plan should ALWAYS begin
|
|
8
|
+
with a step to write or update requirements. Implementation starts with
|
|
9
|
+
requirements, so your plan should too.
|
|
10
|
+
|
|
7
11
|
**ALWAYS follow this workflow when changing system behavior** (new features, bug fixes, any behavioral change). Only pure refactoring (same behavior, different code) may skip requirements.
|
|
8
12
|
|
|
9
13
|
1. **Find or write requirements** - Check `.requirements/` for existing specs; write new ones if needed
|