@popoverai/dotrequirements 0.12.1 → 0.14.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 +66 -61
- package/dist/cli.js +0 -10
- package/dist/commands/init.js +167 -224
- package/dist/commands/link.d.ts +9 -10
- package/dist/commands/link.js +81 -106
- package/dist/commands/mcp-setup.js +77 -94
- package/dist/commands/pull.js +17 -45
- package/dist/commands/push.js +9 -24
- package/dist/convex.d.ts +3 -0
- package/dist/convex.js +3 -0
- package/dist/harness/cache.d.ts +0 -5
- package/dist/harness/cache.js +0 -48
- package/dist/harness/convexReporting.js +7 -14
- package/dist/harness/finalize.d.ts +8 -2
- package/dist/harness/finalize.js +48 -31
- package/dist/harness/prepare.js +7 -9
- package/dist/mcp/convexClient.d.ts +5 -1
- package/dist/mcp/convexClient.js +14 -34
- package/dist/mcp/index.js +25 -78
- package/dist/schema/conversions.d.ts +2 -2
- package/dist/schema/conversions.js +2 -3
- package/dist/schema/schemas.d.ts +14 -37
- package/dist/schema/schemas.js +7 -10
- package/dist/schema/test-schema.js +1 -1
- package/dist/templates/context-file-section.md +59 -0
- package/dist/utils/context-file.d.ts +38 -0
- package/dist/utils/context-file.js +94 -0
- package/dist/utils/env.d.ts +0 -13
- package/dist/utils/env.js +0 -19
- package/dist/utils/gitignore.d.ts +2 -2
- package/dist/utils/gitignore.js +4 -4
- package/dist/utils/oauth-flow.d.ts +0 -1
- package/dist/utils/oauth-flow.js +0 -9
- package/dist/utils/project-discovery.d.ts +3 -5
- package/dist/utils/project-discovery.js +18 -42
- package/dist/utils/project-selector.d.ts +17 -3
- package/dist/utils/project-selector.js +37 -3
- package/dist/utils/project-settings.d.ts +47 -0
- package/dist/utils/project-settings.js +110 -0
- package/dist/utils/templates.d.ts +0 -24
- package/dist/utils/templates.js +0 -39
- package/package.json +1 -1
- package/dist/harness/localReporting.d.ts +0 -6
- package/dist/harness/localReporting.js +0 -49
- package/dist/templates/antigravity-gemini.md +0 -3
- package/dist/templates/antigravity-overview-rule.md +0 -3
- package/dist/templates/antigravity-test-rule.md +0 -3
- package/dist/templates/behavioral-core.md +0 -25
- package/dist/templates/claude-code-overview-skill.md +0 -6
- package/dist/templates/claude-code-skill.md +0 -6
- package/dist/templates/claude-code-test-skill.md +0 -6
- package/dist/templates/codex-agents.md +0 -3
- package/dist/templates/codex-overview-agents.md +0 -3
- package/dist/templates/codex-test-agents.md +0 -3
- package/dist/templates/cursor-overview-rule.mdc +0 -5
- package/dist/templates/cursor-rule.mdc +0 -5
- package/dist/templates/cursor-test-rule.mdc +0 -5
- package/dist/templates/overview-core.md +0 -27
- package/dist/templates/test-writing-core.md +0 -72
- package/dist/utils/detect-existing-project.d.ts +0 -5
- package/dist/utils/detect-existing-project.js +0 -34
|
@@ -48,10 +48,10 @@ export declare function convexToRequirements(convexReqs: ConvexRequirement[]): R
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function requirementsToConvex(nodes: RequirementNode[], projectId: string, documentId: string): ConvexRequirement[];
|
|
50
50
|
/**
|
|
51
|
-
* Build metadata
|
|
51
|
+
* Build metadata for a requirements file.
|
|
52
52
|
* DOC-HEADER-11.3: When pulling, include defaultPrefix in the frontmatter.
|
|
53
53
|
*/
|
|
54
|
-
export declare function buildMetadata(
|
|
54
|
+
export declare function buildMetadata(version?: number, document?: {
|
|
55
55
|
id?: string;
|
|
56
56
|
title: string;
|
|
57
57
|
defaultPrefix?: string;
|
|
@@ -166,12 +166,11 @@ function flattenNode(node, projectId, rootId, position, result, documentId) {
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
|
-
* Build metadata
|
|
169
|
+
* Build metadata for a requirements file.
|
|
170
170
|
* DOC-HEADER-11.3: When pulling, include defaultPrefix in the frontmatter.
|
|
171
171
|
*/
|
|
172
|
-
export function buildMetadata(
|
|
172
|
+
export function buildMetadata(version = 1, document) {
|
|
173
173
|
return {
|
|
174
|
-
projectId,
|
|
175
174
|
pulledAt: new Date().toISOString(),
|
|
176
175
|
version,
|
|
177
176
|
document,
|
package/dist/schema/schemas.d.ts
CHANGED
|
@@ -54,13 +54,12 @@ export declare function parseRequirementKey(key: string): {
|
|
|
54
54
|
*/
|
|
55
55
|
export declare function buildRequirementKey(prefix: string, number: number): string;
|
|
56
56
|
/**
|
|
57
|
-
* Metadata block at the top of every requirements file
|
|
57
|
+
* Metadata block at the top of every requirements file.
|
|
58
|
+
* Only document.title is required for push; everything else is optional or auto-filled.
|
|
58
59
|
*/
|
|
59
60
|
export declare const MetadataSchema: z.ZodObject<{
|
|
60
|
-
|
|
61
|
-
projectSlug: z.ZodOptional<z.ZodString>;
|
|
61
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
62
62
|
pulledAt: z.ZodOptional<z.ZodString>;
|
|
63
|
-
version: z.ZodNumber;
|
|
64
63
|
document: z.ZodOptional<z.ZodObject<{
|
|
65
64
|
id: z.ZodOptional<z.ZodString>;
|
|
66
65
|
title: z.ZodString;
|
|
@@ -75,9 +74,7 @@ export declare const MetadataSchema: z.ZodObject<{
|
|
|
75
74
|
defaultPrefix?: string | undefined;
|
|
76
75
|
}>>;
|
|
77
76
|
}, "strip", z.ZodTypeAny, {
|
|
78
|
-
version
|
|
79
|
-
projectId: string;
|
|
80
|
-
projectSlug?: string | undefined;
|
|
77
|
+
version?: number | undefined;
|
|
81
78
|
pulledAt?: string | undefined;
|
|
82
79
|
document?: {
|
|
83
80
|
title: string;
|
|
@@ -85,9 +82,7 @@ export declare const MetadataSchema: z.ZodObject<{
|
|
|
85
82
|
defaultPrefix?: string | undefined;
|
|
86
83
|
} | undefined;
|
|
87
84
|
}, {
|
|
88
|
-
version
|
|
89
|
-
projectId: string;
|
|
90
|
-
projectSlug?: string | undefined;
|
|
85
|
+
version?: number | undefined;
|
|
91
86
|
pulledAt?: string | undefined;
|
|
92
87
|
document?: {
|
|
93
88
|
title: string;
|
|
@@ -149,10 +144,8 @@ export type RequirementNode = {
|
|
|
149
144
|
*/
|
|
150
145
|
export declare const RequirementsFileSchema: z.ZodObject<{
|
|
151
146
|
_meta: z.ZodObject<{
|
|
152
|
-
|
|
153
|
-
projectSlug: z.ZodOptional<z.ZodString>;
|
|
147
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
154
148
|
pulledAt: z.ZodOptional<z.ZodString>;
|
|
155
|
-
version: z.ZodNumber;
|
|
156
149
|
document: z.ZodOptional<z.ZodObject<{
|
|
157
150
|
id: z.ZodOptional<z.ZodString>;
|
|
158
151
|
title: z.ZodString;
|
|
@@ -167,9 +160,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
167
160
|
defaultPrefix?: string | undefined;
|
|
168
161
|
}>>;
|
|
169
162
|
}, "strip", z.ZodTypeAny, {
|
|
170
|
-
version
|
|
171
|
-
projectId: string;
|
|
172
|
-
projectSlug?: string | undefined;
|
|
163
|
+
version?: number | undefined;
|
|
173
164
|
pulledAt?: string | undefined;
|
|
174
165
|
document?: {
|
|
175
166
|
title: string;
|
|
@@ -177,9 +168,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
177
168
|
defaultPrefix?: string | undefined;
|
|
178
169
|
} | undefined;
|
|
179
170
|
}, {
|
|
180
|
-
version
|
|
181
|
-
projectId: string;
|
|
182
|
-
projectSlug?: string | undefined;
|
|
171
|
+
version?: number | undefined;
|
|
183
172
|
pulledAt?: string | undefined;
|
|
184
173
|
document?: {
|
|
185
174
|
title: string;
|
|
@@ -189,10 +178,8 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
189
178
|
}>;
|
|
190
179
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
191
180
|
_meta: z.ZodObject<{
|
|
192
|
-
|
|
193
|
-
projectSlug: z.ZodOptional<z.ZodString>;
|
|
181
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
194
182
|
pulledAt: z.ZodOptional<z.ZodString>;
|
|
195
|
-
version: z.ZodNumber;
|
|
196
183
|
document: z.ZodOptional<z.ZodObject<{
|
|
197
184
|
id: z.ZodOptional<z.ZodString>;
|
|
198
185
|
title: z.ZodString;
|
|
@@ -207,9 +194,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
207
194
|
defaultPrefix?: string | undefined;
|
|
208
195
|
}>>;
|
|
209
196
|
}, "strip", z.ZodTypeAny, {
|
|
210
|
-
version
|
|
211
|
-
projectId: string;
|
|
212
|
-
projectSlug?: string | undefined;
|
|
197
|
+
version?: number | undefined;
|
|
213
198
|
pulledAt?: string | undefined;
|
|
214
199
|
document?: {
|
|
215
200
|
title: string;
|
|
@@ -217,9 +202,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
217
202
|
defaultPrefix?: string | undefined;
|
|
218
203
|
} | undefined;
|
|
219
204
|
}, {
|
|
220
|
-
version
|
|
221
|
-
projectId: string;
|
|
222
|
-
projectSlug?: string | undefined;
|
|
205
|
+
version?: number | undefined;
|
|
223
206
|
pulledAt?: string | undefined;
|
|
224
207
|
document?: {
|
|
225
208
|
title: string;
|
|
@@ -229,10 +212,8 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
229
212
|
}>;
|
|
230
213
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
231
214
|
_meta: z.ZodObject<{
|
|
232
|
-
|
|
233
|
-
projectSlug: z.ZodOptional<z.ZodString>;
|
|
215
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
234
216
|
pulledAt: z.ZodOptional<z.ZodString>;
|
|
235
|
-
version: z.ZodNumber;
|
|
236
217
|
document: z.ZodOptional<z.ZodObject<{
|
|
237
218
|
id: z.ZodOptional<z.ZodString>;
|
|
238
219
|
title: z.ZodString;
|
|
@@ -247,9 +228,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
247
228
|
defaultPrefix?: string | undefined;
|
|
248
229
|
}>>;
|
|
249
230
|
}, "strip", z.ZodTypeAny, {
|
|
250
|
-
version
|
|
251
|
-
projectId: string;
|
|
252
|
-
projectSlug?: string | undefined;
|
|
231
|
+
version?: number | undefined;
|
|
253
232
|
pulledAt?: string | undefined;
|
|
254
233
|
document?: {
|
|
255
234
|
title: string;
|
|
@@ -257,9 +236,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
|
|
|
257
236
|
defaultPrefix?: string | undefined;
|
|
258
237
|
} | undefined;
|
|
259
238
|
}, {
|
|
260
|
-
version
|
|
261
|
-
projectId: string;
|
|
262
|
-
projectSlug?: string | undefined;
|
|
239
|
+
version?: number | undefined;
|
|
263
240
|
pulledAt?: string | undefined;
|
|
264
241
|
document?: {
|
|
265
242
|
title: string;
|
package/dist/schema/schemas.js
CHANGED
|
@@ -104,19 +104,16 @@ export function buildRequirementKey(prefix, number) {
|
|
|
104
104
|
// Metadata Schema
|
|
105
105
|
// =============================================================================
|
|
106
106
|
/**
|
|
107
|
-
* Metadata block at the top of every requirements file
|
|
107
|
+
* Metadata block at the top of every requirements file.
|
|
108
|
+
* Only document.title is required for push; everything else is optional or auto-filled.
|
|
108
109
|
*/
|
|
109
110
|
export const MetadataSchema = z.object({
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
pulledAt: z.string().optional(), // ISO 8601 timestamp - required for CLI files, optional for IDE
|
|
113
|
-
version: z.number(),
|
|
114
|
-
// Optional document association (one file = one document)
|
|
111
|
+
version: z.number().optional(), // Informational - incremented on push
|
|
112
|
+
pulledAt: z.string().optional(), // ISO 8601 timestamp - used for conflict detection
|
|
115
113
|
document: z.object({
|
|
116
|
-
id: z.string().optional(), //
|
|
117
|
-
title: z.string(),
|
|
118
|
-
//
|
|
119
|
-
defaultPrefix: z.string().optional(),
|
|
114
|
+
id: z.string().optional(), // Cloud document link - filled by push when creating
|
|
115
|
+
title: z.string(), // Required for push
|
|
116
|
+
defaultPrefix: z.string().optional(), // Editor hint for new requirement keys
|
|
120
117
|
}).optional(),
|
|
121
118
|
});
|
|
122
119
|
// =============================================================================
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
## Requirements-First Development
|
|
2
|
+
|
|
3
|
+
This project uses dotrequirements for requirements tracking. Requirements live in `.requirements/*.requirements.md`.
|
|
4
|
+
|
|
5
|
+
### Workflow
|
|
6
|
+
|
|
7
|
+
When adding new functionality (not bug fixes or refactoring):
|
|
8
|
+
|
|
9
|
+
1. **Draft requirements** - Write in `.requirements/*.requirements.md`
|
|
10
|
+
2. **Style-check** - Run `mcp__dotrequirements__style_check` on the file
|
|
11
|
+
3. **Get approval** - Present requirements, wait for go-ahead
|
|
12
|
+
4. **Implement** - Build the feature
|
|
13
|
+
5. **Write tests** - Reference requirements with `requirement()`
|
|
14
|
+
6. **Run tests** - Verify everything passes
|
|
15
|
+
7. **Review tests** - Run `mcp__dotrequirements__review_test` to validate coverage
|
|
16
|
+
|
|
17
|
+
### Requirements Syntax
|
|
18
|
+
|
|
19
|
+
```dotrequirements
|
|
20
|
+
REQ-ID: Short description of expected behavior
|
|
21
|
+
0. Given -> Precondition that must be true
|
|
22
|
+
1. When -> Action or trigger
|
|
23
|
+
2. Then -> Expected outcome
|
|
24
|
+
2.0. And -> Additional outcome detail
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- First line: `KEY: description`
|
|
28
|
+
- Criteria: `position. Label -> content` (Given/When/Then structure)
|
|
29
|
+
- Nesting: Indent with 2 spaces, use `x.y` position paths
|
|
30
|
+
- Delimiter: `->` or the arrow character
|
|
31
|
+
|
|
32
|
+
### Test Usage
|
|
33
|
+
|
|
34
|
+
Use `requirement()` AS the test description:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { requirement } from '@popoverai/dotrequirements/test';
|
|
38
|
+
|
|
39
|
+
test(requirement('REQ-ID'), () => { /* test the requirement */ });
|
|
40
|
+
test(requirement('REQ-ID.0'), () => { /* test specific criterion */ });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### MCP Tools
|
|
44
|
+
|
|
45
|
+
**Exploration:**
|
|
46
|
+
- `list_all_requirements` - Overview of all requirements
|
|
47
|
+
- `get_requirement` - Requirement tree with test coverage
|
|
48
|
+
- `search_requirements` - Search by text/regex
|
|
49
|
+
|
|
50
|
+
**Authoring:**
|
|
51
|
+
- `create_requirement_document` - Get template with format guidance
|
|
52
|
+
- `validate_requirements` - Check syntax (works offline)
|
|
53
|
+
- `style_check` - AI feedback on clarity
|
|
54
|
+
- `push_requirements` - Sync to cloud
|
|
55
|
+
|
|
56
|
+
**Testing:**
|
|
57
|
+
- `get_requirements_by_test` - See requirements a test file covers
|
|
58
|
+
- `list_untested_requirements` - Find gaps in coverage
|
|
59
|
+
- `review_test` - Validate tests match requirement intent
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the appropriate context file name for a platform
|
|
3
|
+
*/
|
|
4
|
+
export declare function getContextFileName(platform: string): string | null;
|
|
5
|
+
/**
|
|
6
|
+
* Find the git root directory
|
|
7
|
+
*/
|
|
8
|
+
export declare function findGitRoot(): Promise<string | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Find the dotrequirements section in file content
|
|
11
|
+
* Returns the start/end indices and content, or null if not found
|
|
12
|
+
*/
|
|
13
|
+
export declare function findDotrequirementsSection(content: string): {
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
content: string;
|
|
17
|
+
} | null;
|
|
18
|
+
/**
|
|
19
|
+
* Wrap content in section markers
|
|
20
|
+
*/
|
|
21
|
+
export declare function wrapInSectionMarkers(content: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Append or update the dotrequirements section in a context file
|
|
24
|
+
* - If file doesn't exist, creates it with just the section
|
|
25
|
+
* - If file exists without section, appends section at end
|
|
26
|
+
* - If file exists with section, replaces the section
|
|
27
|
+
*
|
|
28
|
+
* Returns: { action: 'created' | 'appended' | 'updated', existingContent?: string }
|
|
29
|
+
*/
|
|
30
|
+
export declare function appendOrUpdateSection(filePath: string, sectionContent: string): {
|
|
31
|
+
action: 'created' | 'appended' | 'updated';
|
|
32
|
+
existingContent?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get the full path to the context file for a platform
|
|
36
|
+
*/
|
|
37
|
+
export declare function getContextFilePath(platform: string): Promise<string | null>;
|
|
38
|
+
//# sourceMappingURL=context-file.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { findUp } from 'find-up';
|
|
4
|
+
const SECTION_START = '<!-- dotrequirements:start -->';
|
|
5
|
+
const SECTION_END = '<!-- dotrequirements:end -->';
|
|
6
|
+
/**
|
|
7
|
+
* Platform to context file mapping
|
|
8
|
+
*/
|
|
9
|
+
const PLATFORM_CONTEXT_FILES = {
|
|
10
|
+
'claude-code': 'CLAUDE.md',
|
|
11
|
+
'cursor': 'AGENTS.md',
|
|
12
|
+
'codex': 'AGENTS.md',
|
|
13
|
+
'github-copilot': 'AGENTS.md',
|
|
14
|
+
'antigravity': 'GEMINI.md',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Get the appropriate context file name for a platform
|
|
18
|
+
*/
|
|
19
|
+
export function getContextFileName(platform) {
|
|
20
|
+
return PLATFORM_CONTEXT_FILES[platform] ?? null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Find the git root directory
|
|
24
|
+
*/
|
|
25
|
+
export async function findGitRoot() {
|
|
26
|
+
const gitDir = await findUp('.git', { type: 'directory' });
|
|
27
|
+
return gitDir ? dirname(gitDir) : null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Find the dotrequirements section in file content
|
|
31
|
+
* Returns the start/end indices and content, or null if not found
|
|
32
|
+
*/
|
|
33
|
+
export function findDotrequirementsSection(content) {
|
|
34
|
+
const startIdx = content.indexOf(SECTION_START);
|
|
35
|
+
if (startIdx === -1)
|
|
36
|
+
return null;
|
|
37
|
+
const endIdx = content.indexOf(SECTION_END, startIdx);
|
|
38
|
+
if (endIdx === -1)
|
|
39
|
+
return null;
|
|
40
|
+
return {
|
|
41
|
+
start: startIdx,
|
|
42
|
+
end: endIdx + SECTION_END.length,
|
|
43
|
+
content: content.slice(startIdx, endIdx + SECTION_END.length),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrap content in section markers
|
|
48
|
+
*/
|
|
49
|
+
export function wrapInSectionMarkers(content) {
|
|
50
|
+
return `${SECTION_START}\n${content}\n${SECTION_END}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Append or update the dotrequirements section in a context file
|
|
54
|
+
* - If file doesn't exist, creates it with just the section
|
|
55
|
+
* - If file exists without section, appends section at end
|
|
56
|
+
* - If file exists with section, replaces the section
|
|
57
|
+
*
|
|
58
|
+
* Returns: { action: 'created' | 'appended' | 'updated', existingContent?: string }
|
|
59
|
+
*/
|
|
60
|
+
export function appendOrUpdateSection(filePath, sectionContent) {
|
|
61
|
+
const wrappedContent = wrapInSectionMarkers(sectionContent);
|
|
62
|
+
if (!existsSync(filePath)) {
|
|
63
|
+
// Create new file with just the section
|
|
64
|
+
writeFileSync(filePath, wrappedContent + '\n', 'utf-8');
|
|
65
|
+
return { action: 'created' };
|
|
66
|
+
}
|
|
67
|
+
const existingFile = readFileSync(filePath, 'utf-8');
|
|
68
|
+
const existingSection = findDotrequirementsSection(existingFile);
|
|
69
|
+
if (!existingSection) {
|
|
70
|
+
// Append section to end of file
|
|
71
|
+
const separator = existingFile.endsWith('\n') ? '\n' : '\n\n';
|
|
72
|
+
writeFileSync(filePath, existingFile + separator + wrappedContent + '\n', 'utf-8');
|
|
73
|
+
return { action: 'appended' };
|
|
74
|
+
}
|
|
75
|
+
// Replace existing section
|
|
76
|
+
const newContent = existingFile.slice(0, existingSection.start) +
|
|
77
|
+
wrappedContent +
|
|
78
|
+
existingFile.slice(existingSection.end);
|
|
79
|
+
writeFileSync(filePath, newContent, 'utf-8');
|
|
80
|
+
return { action: 'updated', existingContent: existingSection.content };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the full path to the context file for a platform
|
|
84
|
+
*/
|
|
85
|
+
export async function getContextFilePath(platform) {
|
|
86
|
+
const fileName = getContextFileName(platform);
|
|
87
|
+
if (!fileName)
|
|
88
|
+
return null;
|
|
89
|
+
const gitRoot = await findGitRoot();
|
|
90
|
+
if (!gitRoot)
|
|
91
|
+
return null;
|
|
92
|
+
return join(gitRoot, fileName);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=context-file.js.map
|
package/dist/utils/env.d.ts
CHANGED
|
@@ -5,17 +5,4 @@
|
|
|
5
5
|
* @param force - If true, overwrite existing process.env values (used when reloading after init)
|
|
6
6
|
*/
|
|
7
7
|
export declare function loadEnvFile(cwd?: string, force?: boolean): void;
|
|
8
|
-
/**
|
|
9
|
-
* Get project credentials from environment
|
|
10
|
-
* Returns undefined if not found
|
|
11
|
-
*/
|
|
12
|
-
export declare function getProjectCredentials(): {
|
|
13
|
-
projectId: string;
|
|
14
|
-
projectSecret: string;
|
|
15
|
-
} | undefined;
|
|
16
|
-
/**
|
|
17
|
-
* Get project ID from environment
|
|
18
|
-
* Returns undefined if not found
|
|
19
|
-
*/
|
|
20
|
-
export declare function getProjectId(): string | undefined;
|
|
21
8
|
//# sourceMappingURL=env.d.ts.map
|
package/dist/utils/env.js
CHANGED
|
@@ -36,23 +36,4 @@ export function loadEnvFile(cwd = process.cwd(), force = false) {
|
|
|
36
36
|
// The commands will error appropriately if env vars are missing
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Get project credentials from environment
|
|
41
|
-
* Returns undefined if not found
|
|
42
|
-
*/
|
|
43
|
-
export function getProjectCredentials() {
|
|
44
|
-
const projectId = process.env.DOTREQUIREMENTS_PROJECT_ID;
|
|
45
|
-
const projectSecret = process.env.DOTREQUIREMENTS_PROJECT_SECRET;
|
|
46
|
-
if (!projectId || !projectSecret) {
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
return { projectId, projectSecret };
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Get project ID from environment
|
|
53
|
-
* Returns undefined if not found
|
|
54
|
-
*/
|
|
55
|
-
export function getProjectId() {
|
|
56
|
-
return process.env.DOTREQUIREMENTS_PROJECT_ID;
|
|
57
|
-
}
|
|
58
39
|
//# sourceMappingURL=env.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ensure .
|
|
2
|
+
* Ensure project-settings.json is in .gitignore
|
|
3
3
|
* Creates .gitignore if it doesn't exist
|
|
4
|
-
* Appends
|
|
4
|
+
* Appends the entry if not already present
|
|
5
5
|
*/
|
|
6
6
|
export declare function ensureGitignore(cwd?: string): void;
|
|
7
7
|
//# sourceMappingURL=gitignore.d.ts.map
|
package/dist/utils/gitignore.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
/**
|
|
4
|
-
* Ensure .
|
|
4
|
+
* Ensure project-settings.json is in .gitignore
|
|
5
5
|
* Creates .gitignore if it doesn't exist
|
|
6
|
-
* Appends
|
|
6
|
+
* Appends the entry if not already present
|
|
7
7
|
*/
|
|
8
8
|
export function ensureGitignore(cwd = process.cwd()) {
|
|
9
9
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
10
|
-
const entryToAdd = '.
|
|
10
|
+
const entryToAdd = '.requirements/project-settings.json';
|
|
11
11
|
const comment = '# dotrequirements credentials';
|
|
12
12
|
// Check if .gitignore exists
|
|
13
13
|
if (!fs.existsSync(gitignorePath)) {
|
|
@@ -17,7 +17,7 @@ export function ensureGitignore(cwd = process.cwd()) {
|
|
|
17
17
|
}
|
|
18
18
|
// Read existing .gitignore
|
|
19
19
|
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
20
|
-
// Check if
|
|
20
|
+
// Check if entry is already present
|
|
21
21
|
if (content.includes(entryToAdd)) {
|
|
22
22
|
return; // Already present, nothing to do
|
|
23
23
|
}
|
|
@@ -16,7 +16,6 @@ export interface OAuthResult {
|
|
|
16
16
|
* 3. Open browser for user authentication
|
|
17
17
|
* 4. Wait for callback with authorization code
|
|
18
18
|
* 5. Exchange code for tokens via WorkOS SDK
|
|
19
|
-
* 6. Store tokens securely
|
|
20
19
|
*/
|
|
21
20
|
export declare function executeOAuthFlow(): Promise<OAuthResult>;
|
|
22
21
|
//# sourceMappingURL=oauth-flow.d.ts.map
|
package/dist/utils/oauth-flow.js
CHANGED
|
@@ -8,7 +8,6 @@ import { randomBytes } from 'crypto';
|
|
|
8
8
|
import { ConvexHttpClient } from 'convex/browser';
|
|
9
9
|
import { startCallbackServer } from './oauth-callback-server.js';
|
|
10
10
|
import { openBrowserWithFallback } from './browser-launch.js';
|
|
11
|
-
import { storeTokens } from './token-storage.js';
|
|
12
11
|
import { getConvexUrl } from '../config.js';
|
|
13
12
|
import { api } from '../convex.js';
|
|
14
13
|
const WORKOS_CLIENT_ID = 'client_01KCM6WPYX201P7DRV5DHF4TR7';
|
|
@@ -20,7 +19,6 @@ const WORKOS_AUTHORIZE_URL = 'https://api.workos.com/user_management/authorize';
|
|
|
20
19
|
* 3. Open browser for user authentication
|
|
21
20
|
* 4. Wait for callback with authorization code
|
|
22
21
|
* 5. Exchange code for tokens via WorkOS SDK
|
|
23
|
-
* 6. Store tokens securely
|
|
24
22
|
*/
|
|
25
23
|
export async function executeOAuthFlow() {
|
|
26
24
|
// Generate PKCE code verifier and challenge
|
|
@@ -58,13 +56,6 @@ export async function executeOAuthFlow() {
|
|
|
58
56
|
throw new Error(`Token exchange failed: ${result.error}`);
|
|
59
57
|
}
|
|
60
58
|
const { accessToken, refreshToken, expiresAt } = result;
|
|
61
|
-
// Store tokens securely
|
|
62
|
-
const tokens = {
|
|
63
|
-
accessToken,
|
|
64
|
-
refreshToken,
|
|
65
|
-
expiresAt,
|
|
66
|
-
};
|
|
67
|
-
await storeTokens(tokens);
|
|
68
59
|
console.log('✓ Authentication complete!');
|
|
69
60
|
return {
|
|
70
61
|
accessToken,
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
export interface DotreqProject {
|
|
5
5
|
/** Absolute path to the project root directory */
|
|
6
6
|
path: string;
|
|
7
|
-
/** Project ID from .
|
|
7
|
+
/** Project ID from project-settings.json */
|
|
8
8
|
projectId: string;
|
|
9
|
-
/** Project secret from .
|
|
9
|
+
/** Project secret from project-settings.json */
|
|
10
10
|
projectSecret: string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
@@ -24,9 +24,7 @@ export type ProjectDiscoveryResult = {
|
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
26
|
* Check if a directory is a configured dotrequirements project
|
|
27
|
-
* A project is configured if
|
|
28
|
-
* 1. It has a package.json that includes "dotreq" (in dependencies or devDependencies)
|
|
29
|
-
* 2. It has a .env.local with DOTREQUIREMENTS_PROJECT_ID and DOTREQUIREMENTS_PROJECT_SECRET
|
|
27
|
+
* A project is configured if it has .requirements/project-settings.json with credentials
|
|
30
28
|
*/
|
|
31
29
|
export declare function isConfiguredProject(dir: string): DotreqProject | null;
|
|
32
30
|
/**
|
|
@@ -1,58 +1,34 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { glob } from 'glob';
|
|
4
|
+
import { readProjectSettings } from './project-settings.js';
|
|
4
5
|
/**
|
|
5
6
|
* Check if a directory is a configured dotrequirements project
|
|
6
|
-
* A project is configured if
|
|
7
|
-
* 1. It has a package.json that includes "dotreq" (in dependencies or devDependencies)
|
|
8
|
-
* 2. It has a .env.local with DOTREQUIREMENTS_PROJECT_ID and DOTREQUIREMENTS_PROJECT_SECRET
|
|
7
|
+
* A project is configured if it has .requirements/project-settings.json with credentials
|
|
9
8
|
*/
|
|
10
9
|
export function isConfiguredProject(dir) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!fs.existsSync(pkgPath)) {
|
|
10
|
+
const requirementsDir = path.join(dir, '.requirements');
|
|
11
|
+
// Check for .requirements/ directory
|
|
12
|
+
if (!fs.existsSync(requirementsDir) || !fs.statSync(requirementsDir).isDirectory()) {
|
|
15
13
|
return null;
|
|
16
14
|
}
|
|
15
|
+
// Try to read credentials from project-settings.json
|
|
16
|
+
let settings;
|
|
17
17
|
try {
|
|
18
|
-
|
|
19
|
-
// Look for "dotreq" anywhere in the file (flexible matching)
|
|
20
|
-
if (!pkgContent.includes('dotreq')) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
// Check for .env.local with required vars
|
|
28
|
-
if (!fs.existsSync(envPath)) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
let projectId;
|
|
32
|
-
let projectSecret;
|
|
33
|
-
try {
|
|
34
|
-
const envContent = fs.readFileSync(envPath, 'utf-8');
|
|
35
|
-
const lines = envContent.split('\n');
|
|
36
|
-
for (const line of lines) {
|
|
37
|
-
const trimmed = line.trim();
|
|
38
|
-
if (trimmed.startsWith('DOTREQUIREMENTS_PROJECT_ID=')) {
|
|
39
|
-
projectId = trimmed.split('=')[1]?.trim().replace(/['"]/g, '');
|
|
40
|
-
}
|
|
41
|
-
if (trimmed.startsWith('DOTREQUIREMENTS_PROJECT_SECRET=')) {
|
|
42
|
-
projectSecret = trimmed.split('=')[1]?.trim().replace(/['"]/g, '');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
18
|
+
settings = readProjectSettings(dir);
|
|
45
19
|
}
|
|
46
20
|
catch {
|
|
21
|
+
// Invalid settings file
|
|
47
22
|
return null;
|
|
48
23
|
}
|
|
49
|
-
if (!
|
|
24
|
+
if (!settings) {
|
|
25
|
+
// No credentials (local-only project)
|
|
50
26
|
return null;
|
|
51
27
|
}
|
|
52
28
|
return {
|
|
53
29
|
path: dir,
|
|
54
|
-
projectId,
|
|
55
|
-
projectSecret,
|
|
30
|
+
projectId: settings.projectId,
|
|
31
|
+
projectSecret: settings.projectSecret,
|
|
56
32
|
};
|
|
57
33
|
}
|
|
58
34
|
/**
|
|
@@ -61,14 +37,14 @@ export function isConfiguredProject(dir) {
|
|
|
61
37
|
*/
|
|
62
38
|
export async function findConfiguredProjects(startDir) {
|
|
63
39
|
const projects = [];
|
|
64
|
-
// Find all
|
|
65
|
-
const
|
|
40
|
+
// Find all .requirements directories
|
|
41
|
+
const requirementsDirs = await glob('**/.requirements', {
|
|
66
42
|
cwd: startDir,
|
|
67
43
|
absolute: true,
|
|
68
44
|
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/build/**'],
|
|
69
45
|
});
|
|
70
|
-
for (const
|
|
71
|
-
const dir = path.dirname(
|
|
46
|
+
for (const reqDir of requirementsDirs) {
|
|
47
|
+
const dir = path.dirname(reqDir);
|
|
72
48
|
const project = isConfiguredProject(dir);
|
|
73
49
|
if (project) {
|
|
74
50
|
projects.push(project);
|
|
@@ -96,7 +72,7 @@ export async function discoverProjects(workingDir = process.cwd()) {
|
|
|
96
72
|
if (childProjects.length === 0) {
|
|
97
73
|
return {
|
|
98
74
|
type: 'none',
|
|
99
|
-
message: `No dotrequirements projects found in ${workingDir} or its children.\n\nA project must have:\n1.
|
|
75
|
+
message: `No dotrequirements projects found in ${workingDir} or its children.\n\nA project must have:\n1. A .requirements/ directory\n2. Cloud credentials in .requirements/project-settings.json\n\nRun "dotrequirements init" to create a project or "dotrequirements link" to connect to cloud.`,
|
|
100
76
|
};
|
|
101
77
|
}
|
|
102
78
|
// Case 3: Exactly one child project
|