@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
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
|
|
8
8
|
* 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
|
|
9
9
|
*/
|
|
10
|
-
import { type RequirementNode } from
|
|
11
|
-
import { Requirement } from
|
|
10
|
+
import { type RequirementNode } from "../schema/index.js";
|
|
11
|
+
import type { Requirement } from "./types.js";
|
|
12
12
|
export declare const loadedRequirements: Map<string, RequirementNode>;
|
|
13
13
|
export interface LoadOptions {
|
|
14
14
|
/** Explicit project root path. Takes precedence over env var and cached file. */
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
|
|
8
8
|
* 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
|
|
9
9
|
*/
|
|
10
|
-
import * as fs from
|
|
11
|
-
import * as path from
|
|
12
|
-
import {
|
|
13
|
-
import { parseRequirementsFromFile, resolveRequirementPath, } from
|
|
14
|
-
import { findCachedProjectRoot, readLookupCache
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { findRequirementsFilesSync } from "../requirements/index.js";
|
|
13
|
+
import { parseRequirementsFromFile, resolveRequirementPath, } from "../schema/index.js";
|
|
14
|
+
import { findCachedProjectRoot, readLookupCache } from "./cache.js";
|
|
15
15
|
// Cache: Maps full requirement IDs (e.g., "REQ-123.0.1") to RequirementNode
|
|
16
16
|
export const loadedRequirements = new Map();
|
|
17
17
|
// Cache: Maps root requirement IDs (e.g., "REQ-123") to their tree
|
|
@@ -36,34 +36,12 @@ function findProjectRoot(options = {}) {
|
|
|
36
36
|
// Priority 3: Cached file (written by prepare())
|
|
37
37
|
return findCachedProjectRoot(process.cwd());
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Find all *.requirements.md files under project root synchronously.
|
|
41
|
-
*/
|
|
42
|
-
function findRequirementsFilesSync(projectRoot) {
|
|
43
|
-
const pattern = '**/*.requirements.md';
|
|
44
|
-
const matches = globSync(pattern, {
|
|
45
|
-
cwd: projectRoot,
|
|
46
|
-
ignore: [
|
|
47
|
-
'**/node_modules/**',
|
|
48
|
-
'**/dist/**',
|
|
49
|
-
'**/.git/**',
|
|
50
|
-
'**/build/**',
|
|
51
|
-
'**/example/**',
|
|
52
|
-
'**/examples/**',
|
|
53
|
-
'**/__tests__/**',
|
|
54
|
-
'**/fixtures/**',
|
|
55
|
-
'**/.fixtures/**',
|
|
56
|
-
],
|
|
57
|
-
dot: true, // Include dotfiles/dotdirs like .requirements/
|
|
58
|
-
});
|
|
59
|
-
return matches.map(m => path.join(projectRoot, m));
|
|
60
|
-
}
|
|
61
39
|
/**
|
|
62
40
|
* Try to load requirements from lookup cache (fast path).
|
|
63
41
|
* Returns true if cache was loaded, false if not available.
|
|
64
42
|
*/
|
|
65
43
|
function tryLoadFromCache(projectRoot) {
|
|
66
|
-
const requirementsDir = path.join(projectRoot,
|
|
44
|
+
const requirementsDir = path.join(projectRoot, ".requirements");
|
|
67
45
|
if (!fs.existsSync(requirementsDir)) {
|
|
68
46
|
return false;
|
|
69
47
|
}
|
|
@@ -82,7 +60,7 @@ function tryLoadFromCache(projectRoot) {
|
|
|
82
60
|
};
|
|
83
61
|
loadedRequirements.set(id, node);
|
|
84
62
|
// If this is a root node (no dot in ID), add to trees cache
|
|
85
|
-
if (!id.includes(
|
|
63
|
+
if (!id.includes(".")) {
|
|
86
64
|
requirementTrees.set(id, node);
|
|
87
65
|
}
|
|
88
66
|
}
|
|
@@ -96,7 +74,7 @@ function flattenRequirementTree(node) {
|
|
|
96
74
|
// Add this node to the flat cache
|
|
97
75
|
loadedRequirements.set(node.id, node);
|
|
98
76
|
// If this is a root node (no dot in ID), also add to trees cache
|
|
99
|
-
if (!node.id.includes(
|
|
77
|
+
if (!node.id.includes(".")) {
|
|
100
78
|
requirementTrees.set(node.id, node);
|
|
101
79
|
}
|
|
102
80
|
// Recursively add children
|
|
@@ -119,8 +97,8 @@ export function loadRequirements(options = {}) {
|
|
|
119
97
|
// Find project root
|
|
120
98
|
const projectRoot = findProjectRoot(options);
|
|
121
99
|
if (!projectRoot) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
|
|
100
|
+
throw new Error("Could not find project root. " +
|
|
101
|
+
"Set DOTREQUIREMENTS_PROJECT_ROOT environment variable or run prepare() in globalSetup.");
|
|
124
102
|
}
|
|
125
103
|
// Try cache first (fast path)
|
|
126
104
|
if (tryLoadFromCache(projectRoot)) {
|
|
@@ -128,8 +106,8 @@ export function loadRequirements(options = {}) {
|
|
|
128
106
|
}
|
|
129
107
|
// Fallback: Parse .requirements.md files on-demand
|
|
130
108
|
if (!warnedAboutFallback && !options.projectRoot) {
|
|
131
|
-
console.warn(
|
|
132
|
-
|
|
109
|
+
console.warn("⚠️ Performance warning: prepare() was not called. " +
|
|
110
|
+
"Parsing requirements on-demand. Run prepare() in globalSetup for faster tests.");
|
|
133
111
|
warnedAboutFallback = true;
|
|
134
112
|
}
|
|
135
113
|
// Find all .requirements.md files under the project root
|
|
@@ -144,7 +122,7 @@ export function loadRequirements(options = {}) {
|
|
|
144
122
|
}
|
|
145
123
|
}
|
|
146
124
|
catch (error) {
|
|
147
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
125
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
148
126
|
console.error(`Error loading ${file}:`, errorMessage);
|
|
149
127
|
}
|
|
150
128
|
}
|
package/dist/harness/tracking.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* HARNESS-REQUIREMENT-3: Each requirement() call is recorded for coverage reporting
|
|
8
8
|
*/
|
|
9
|
-
import * as path from
|
|
10
|
-
import {
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { appendTrackingEntry, clearTrackingFile, findProjectRoot, findRequirementsDir, getCacheDir, getTestRunId, initTestRunId, } from "./cache.js";
|
|
11
11
|
const trackedRequirements = new Map();
|
|
12
12
|
// Test run tracking
|
|
13
13
|
let testRunId = null;
|
|
@@ -17,7 +17,7 @@ export function ensureTestRun() {
|
|
|
17
17
|
if (!testRunId) {
|
|
18
18
|
// Priority 1: Use environment variable (cross-process persistence from globalSetup)
|
|
19
19
|
if (process.env.DOTREQUIREMENTS_PROJECT_ROOT) {
|
|
20
|
-
requirementsDirCached = path.join(process.env.DOTREQUIREMENTS_PROJECT_ROOT,
|
|
20
|
+
requirementsDirCached = path.join(process.env.DOTREQUIREMENTS_PROJECT_ROOT, ".requirements");
|
|
21
21
|
}
|
|
22
22
|
// Priority 2: Find from caller directory
|
|
23
23
|
if (!requirementsDirCached) {
|
|
@@ -29,7 +29,7 @@ export function ensureTestRun() {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
if (!requirementsDirCached) {
|
|
32
|
-
throw new Error(
|
|
32
|
+
throw new Error("Could not find .requirements directory. Please create one in your project root.");
|
|
33
33
|
}
|
|
34
34
|
// Try to read from shared test run ID file (set by prepare())
|
|
35
35
|
const existingRunId = getTestRunId(requirementsDirCached);
|
|
@@ -48,14 +48,14 @@ export function ensureTestRun() {
|
|
|
48
48
|
// Get caller location for tracking
|
|
49
49
|
export function getCallerLocation() {
|
|
50
50
|
const error = new Error();
|
|
51
|
-
const stack = error.stack?.split(
|
|
51
|
+
const stack = error.stack?.split("\n") || [];
|
|
52
52
|
// Find the first stack frame that's not in our library files
|
|
53
53
|
for (let i = 2; i < stack.length; i++) {
|
|
54
54
|
const frame = stack[i];
|
|
55
|
-
if (!frame.includes(
|
|
56
|
-
!frame.includes(
|
|
57
|
-
!frame.includes(
|
|
58
|
-
!frame.includes(
|
|
55
|
+
if (!frame.includes("/harness/index.ts") &&
|
|
56
|
+
!frame.includes("/harness/index.js") &&
|
|
57
|
+
!frame.includes("/harness/tracking.ts") &&
|
|
58
|
+
!frame.includes("/harness/tracking.js")) {
|
|
59
59
|
// Extract filename and line number
|
|
60
60
|
const regex1 = /\((.+):(\d+):(\d+)\)/;
|
|
61
61
|
const regex2 = /at (.+):(\d+):(\d+)/;
|
|
@@ -67,19 +67,19 @@ export function getCallerLocation() {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
return
|
|
70
|
+
return "unknown";
|
|
71
71
|
}
|
|
72
72
|
// Get caller's directory (full path) for finding .requirements
|
|
73
73
|
function getCallerDirectory() {
|
|
74
74
|
const error = new Error();
|
|
75
|
-
const stack = error.stack?.split(
|
|
75
|
+
const stack = error.stack?.split("\n") || [];
|
|
76
76
|
// Find the first stack frame that's not in our library files
|
|
77
77
|
for (let i = 2; i < stack.length; i++) {
|
|
78
78
|
const frame = stack[i];
|
|
79
|
-
if (!frame.includes(
|
|
80
|
-
!frame.includes(
|
|
81
|
-
!frame.includes(
|
|
82
|
-
!frame.includes(
|
|
79
|
+
if (!frame.includes("/harness/index.ts") &&
|
|
80
|
+
!frame.includes("/harness/index.js") &&
|
|
81
|
+
!frame.includes("/harness/tracking.ts") &&
|
|
82
|
+
!frame.includes("/harness/tracking.js")) {
|
|
83
83
|
// Extract filename
|
|
84
84
|
const regex1 = /\((.+):(\d+):(\d+)\)/;
|
|
85
85
|
const regex2 = /at (.+):(\d+):(\d+)/;
|
|
@@ -154,7 +154,7 @@ export function clearTracking() {
|
|
|
154
154
|
export function initTestRun() {
|
|
155
155
|
const projectRoot = findProjectRoot();
|
|
156
156
|
if (!projectRoot) {
|
|
157
|
-
throw new Error(
|
|
157
|
+
throw new Error("Could not find .requirements directory. Please create one in your project root.");
|
|
158
158
|
}
|
|
159
159
|
const reqDir = findRequirementsDir();
|
|
160
160
|
// Initialize test run ID
|
|
@@ -171,9 +171,9 @@ export function initTestRun() {
|
|
|
171
171
|
export function finalizeTestRun() {
|
|
172
172
|
// Import and call the new finalize function
|
|
173
173
|
// Using dynamic import to avoid circular dependencies
|
|
174
|
-
const { finalize } = require(
|
|
174
|
+
const { finalize } = require("./finalize.js");
|
|
175
175
|
finalize().catch((error) => {
|
|
176
|
-
console.error(
|
|
176
|
+
console.error("Error in finalizeTestRun:", error.message);
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
//# sourceMappingURL=tracking.js.map
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Type definitions for test harness.
|
|
3
3
|
* Uses shared schema types for consistency with CLI and MCP.
|
|
4
4
|
*/
|
|
5
|
-
import type { RequirementNode } from
|
|
5
|
+
import type { RequirementNode } from "../schema/index.js";
|
|
6
6
|
export type { RequirementNode as Requirement };
|
|
7
7
|
export interface CriteriaDetail {
|
|
8
8
|
reqId: string;
|
|
@@ -15,44 +15,5 @@ interface ConvexConfig {
|
|
|
15
15
|
* Returns null if credentials are not configured.
|
|
16
16
|
*/
|
|
17
17
|
export declare function loadConvexConfig(cwd?: string): ConvexConfig | null;
|
|
18
|
-
/**
|
|
19
|
-
* Query Convex for requirement coverage
|
|
20
|
-
*/
|
|
21
|
-
export declare function getRequirementCoverage(requirementKey: string, projectId: string, projectSecret: string, convexUrl: string): Promise<{
|
|
22
|
-
requirementKey: string;
|
|
23
|
-
lastTestedAt: number | null;
|
|
24
|
-
branch: string | null;
|
|
25
|
-
testFile: string | null;
|
|
26
|
-
testLine: number | null;
|
|
27
|
-
allBranches: Array<{
|
|
28
|
-
branch: string;
|
|
29
|
-
lastTestedAt: number;
|
|
30
|
-
testFile?: string;
|
|
31
|
-
testLine?: number;
|
|
32
|
-
}>;
|
|
33
|
-
}>;
|
|
34
|
-
/**
|
|
35
|
-
* Query Convex for project coverage summary
|
|
36
|
-
*/
|
|
37
|
-
export declare function getProjectCoverage(projectId: string, projectSecret: string, convexUrl: string, options?: {
|
|
38
|
-
branch?: string;
|
|
39
|
-
sinceTimestamp?: number;
|
|
40
|
-
}): Promise<{
|
|
41
|
-
tested: Array<{
|
|
42
|
-
requirementKey: string;
|
|
43
|
-
lastTestedAt: number;
|
|
44
|
-
branch: string;
|
|
45
|
-
testFile?: string;
|
|
46
|
-
testLine?: number;
|
|
47
|
-
}>;
|
|
48
|
-
untested: string[];
|
|
49
|
-
}>;
|
|
50
|
-
/**
|
|
51
|
-
* PROJ-CONTEXT-7: Query Convex for project context (AI style guidelines)
|
|
52
|
-
*/
|
|
53
|
-
export declare function getProjectContext(projectId: string, projectSecret: string, convexUrl: string): Promise<{
|
|
54
|
-
projectContext: string | null;
|
|
55
|
-
requirementsStyleContext: string | null;
|
|
56
|
-
} | null>;
|
|
57
18
|
export {};
|
|
58
19
|
//# sourceMappingURL=convexClient.d.ts.map
|
package/dist/mcp/convexClient.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { getProjectCredentials } from
|
|
1
|
+
import { getProjectCredentials } from "../utils/project-settings.js";
|
|
2
2
|
/**
|
|
3
3
|
* Production Convex deployment URL
|
|
4
4
|
*/
|
|
5
|
-
export const CONVEX_URL =
|
|
5
|
+
export const CONVEX_URL = "https://data.dotrequirements.io";
|
|
6
6
|
/**
|
|
7
7
|
* Load Convex configuration from project settings.
|
|
8
8
|
* Returns null if credentials are not configured.
|
|
@@ -21,109 +21,4 @@ export function loadConvexConfig(cwd = process.cwd()) {
|
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Query Convex for requirement coverage
|
|
26
|
-
*/
|
|
27
|
-
export async function getRequirementCoverage(requirementKey, projectId, projectSecret, convexUrl) {
|
|
28
|
-
const response = await fetch(`${convexUrl}/api/query`, {
|
|
29
|
-
method: 'POST',
|
|
30
|
-
headers: { 'Content-Type': 'application/json' },
|
|
31
|
-
body: JSON.stringify({
|
|
32
|
-
path: 'testCoverage/queries:getRequirementCoverage',
|
|
33
|
-
args: {
|
|
34
|
-
projectAuth: {
|
|
35
|
-
projectSlug: projectId,
|
|
36
|
-
projectSecret,
|
|
37
|
-
},
|
|
38
|
-
target: {
|
|
39
|
-
type: 'project',
|
|
40
|
-
slug: projectId,
|
|
41
|
-
},
|
|
42
|
-
requirementKey,
|
|
43
|
-
},
|
|
44
|
-
format: 'json',
|
|
45
|
-
}),
|
|
46
|
-
});
|
|
47
|
-
if (!response.ok) {
|
|
48
|
-
throw new Error(`Failed to query coverage: ${response.statusText}`);
|
|
49
|
-
}
|
|
50
|
-
const json = await response.json();
|
|
51
|
-
if (json.status === 'error') {
|
|
52
|
-
throw new Error(`Convex query failed: ${json.errorMessage}`);
|
|
53
|
-
}
|
|
54
|
-
return json.value;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Query Convex for project coverage summary
|
|
58
|
-
*/
|
|
59
|
-
export async function getProjectCoverage(projectId, projectSecret, convexUrl, options) {
|
|
60
|
-
const response = await fetch(`${convexUrl}/api/query`, {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers: { 'Content-Type': 'application/json' },
|
|
63
|
-
body: JSON.stringify({
|
|
64
|
-
path: 'testCoverage/queries:getProjectCoverage',
|
|
65
|
-
args: {
|
|
66
|
-
projectAuth: {
|
|
67
|
-
projectSlug: projectId,
|
|
68
|
-
projectSecret,
|
|
69
|
-
},
|
|
70
|
-
target: {
|
|
71
|
-
type: 'project',
|
|
72
|
-
slug: projectId,
|
|
73
|
-
},
|
|
74
|
-
branch: options?.branch,
|
|
75
|
-
sinceTimestamp: options?.sinceTimestamp,
|
|
76
|
-
},
|
|
77
|
-
format: 'json',
|
|
78
|
-
}),
|
|
79
|
-
});
|
|
80
|
-
if (!response.ok) {
|
|
81
|
-
throw new Error(`Failed to query coverage: ${response.statusText}`);
|
|
82
|
-
}
|
|
83
|
-
const json = await response.json();
|
|
84
|
-
if (json.status === 'error') {
|
|
85
|
-
throw new Error(`Convex query failed: ${json.errorMessage}`);
|
|
86
|
-
}
|
|
87
|
-
return json.value;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* PROJ-CONTEXT-7: Query Convex for project context (AI style guidelines)
|
|
91
|
-
*/
|
|
92
|
-
export async function getProjectContext(projectId, projectSecret, convexUrl) {
|
|
93
|
-
try {
|
|
94
|
-
const response = await fetch(`${convexUrl}/api/query`, {
|
|
95
|
-
method: 'POST',
|
|
96
|
-
headers: { 'Content-Type': 'application/json' },
|
|
97
|
-
body: JSON.stringify({
|
|
98
|
-
path: 'projects/queries:get',
|
|
99
|
-
args: {
|
|
100
|
-
projectAuth: {
|
|
101
|
-
projectSlug: projectId,
|
|
102
|
-
projectSecret,
|
|
103
|
-
},
|
|
104
|
-
target: {
|
|
105
|
-
type: 'project',
|
|
106
|
-
slug: projectId,
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
format: 'json',
|
|
110
|
-
}),
|
|
111
|
-
});
|
|
112
|
-
if (!response.ok) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
const json = await response.json();
|
|
116
|
-
if (json.status === 'error' || !json.value) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
projectContext: json.value.projectContext ?? null,
|
|
121
|
-
requirementsStyleContext: json.value.requirementsStyleContext ?? null,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
// Non-fatal: return null if query fails
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
24
|
//# sourceMappingURL=convexClient.js.map
|
package/dist/mcp/grep.d.ts
CHANGED
package/dist/mcp/grep.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { execFile } from
|
|
2
|
-
import { promisify } from
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
3
|
const execFileAsync = promisify(execFile);
|
|
4
4
|
/**
|
|
5
5
|
* Check if ripgrep is available
|
|
6
6
|
*/
|
|
7
7
|
async function hasRipgrep() {
|
|
8
8
|
try {
|
|
9
|
-
await execFileAsync(
|
|
9
|
+
await execFileAsync("rg", ["--version"]);
|
|
10
10
|
return true;
|
|
11
11
|
}
|
|
12
12
|
catch {
|
|
@@ -21,7 +21,7 @@ async function hasRipgrep() {
|
|
|
21
21
|
export async function findTestsForRequirement(workspaceRoot, requirementId) {
|
|
22
22
|
const useRg = await hasRipgrep();
|
|
23
23
|
// Escape special regex characters in the ID
|
|
24
|
-
const escapedId = requirementId.replace(/[.*+?^${}()|[\]\\]/g,
|
|
24
|
+
const escapedId = requirementId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
25
|
// Pattern to match requirement('ID'), requirement("ID"), or requirement(`ID`)
|
|
26
26
|
const pattern = `requirement\\s*\\(\\s*['"\`]${escapedId}`;
|
|
27
27
|
try {
|
|
@@ -29,12 +29,34 @@ export async function findTestsForRequirement(workspaceRoot, requirementId) {
|
|
|
29
29
|
if (useRg) {
|
|
30
30
|
// Use ripgrep for better performance
|
|
31
31
|
// Using execFile with array args to avoid shell injection
|
|
32
|
-
const { stdout } = await execFileAsync(
|
|
32
|
+
const { stdout } = await execFileAsync("rg", [
|
|
33
|
+
"-n",
|
|
34
|
+
"--column",
|
|
35
|
+
pattern,
|
|
36
|
+
"--type",
|
|
37
|
+
"ts",
|
|
38
|
+
"--type",
|
|
39
|
+
"js",
|
|
40
|
+
"--type",
|
|
41
|
+
"tsx",
|
|
42
|
+
"--type",
|
|
43
|
+
"jsx",
|
|
44
|
+
workspaceRoot,
|
|
45
|
+
], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // ripgrep returns non-zero on no matches
|
|
33
46
|
output = stdout;
|
|
34
47
|
}
|
|
35
48
|
else {
|
|
36
49
|
// Fall back to grep using execFile
|
|
37
|
-
const { stdout } = await execFileAsync(
|
|
50
|
+
const { stdout } = await execFileAsync("grep", [
|
|
51
|
+
"-rn",
|
|
52
|
+
"--include=*.ts",
|
|
53
|
+
"--include=*.tsx",
|
|
54
|
+
"--include=*.js",
|
|
55
|
+
"--include=*.jsx",
|
|
56
|
+
"-E",
|
|
57
|
+
pattern,
|
|
58
|
+
workspaceRoot,
|
|
59
|
+
], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // grep returns non-zero on no matches
|
|
38
60
|
output = stdout;
|
|
39
61
|
}
|
|
40
62
|
return parseGrepOutput(output, requirementId, useRg);
|
|
@@ -48,11 +70,11 @@ export async function findTestsForRequirement(workspaceRoot, requirementId) {
|
|
|
48
70
|
* Find all requirement references in a specific file using AST parsing
|
|
49
71
|
*/
|
|
50
72
|
export async function findRequirementsInFile(filePath) {
|
|
51
|
-
const { parse } = await import(
|
|
52
|
-
const traverse = await import(
|
|
53
|
-
const t = await import(
|
|
54
|
-
const fs = await import(
|
|
55
|
-
const pathModule = await import(
|
|
73
|
+
const { parse } = await import("@babel/parser");
|
|
74
|
+
const traverse = await import("@babel/traverse");
|
|
75
|
+
const t = await import("@babel/types");
|
|
76
|
+
const fs = await import("node:fs");
|
|
77
|
+
const pathModule = await import("node:path");
|
|
56
78
|
const results = [];
|
|
57
79
|
// Resolve relative paths from current working directory
|
|
58
80
|
let resolvedPath = pathModule.isAbsolute(filePath)
|
|
@@ -60,32 +82,34 @@ export async function findRequirementsInFile(filePath) {
|
|
|
60
82
|
: pathModule.resolve(process.cwd(), filePath);
|
|
61
83
|
// If file doesn't exist and path is relative, try resolving from CLI package root
|
|
62
84
|
if (!pathModule.isAbsolute(filePath) && !fs.existsSync(resolvedPath)) {
|
|
63
|
-
const cliPackageRoot = pathModule.resolve(__dirname,
|
|
85
|
+
const cliPackageRoot = pathModule.resolve(__dirname, "../..");
|
|
64
86
|
const alternativePath = pathModule.resolve(cliPackageRoot, filePath);
|
|
65
87
|
if (fs.existsSync(alternativePath)) {
|
|
66
88
|
resolvedPath = alternativePath;
|
|
67
89
|
}
|
|
68
90
|
}
|
|
69
91
|
try {
|
|
70
|
-
const code = fs.readFileSync(resolvedPath,
|
|
92
|
+
const code = fs.readFileSync(resolvedPath, "utf-8");
|
|
71
93
|
const ast = parse(code, {
|
|
72
|
-
sourceType:
|
|
73
|
-
plugins: [
|
|
94
|
+
sourceType: "module",
|
|
95
|
+
plugins: ["typescript", "jsx"],
|
|
74
96
|
errorRecovery: true,
|
|
75
97
|
});
|
|
76
98
|
// Handle both ES module and CommonJS exports for traverse
|
|
99
|
+
// biome-ignore lint/suspicious/noExplicitAny: @babel/traverse ships a CJS default-export that's a callable, with .default property nested 1-2 levels. The interop dance requires escaping the function-vs-namespace type wall.
|
|
77
100
|
let traverseFunc = traverse;
|
|
78
|
-
if (typeof traverseFunc !==
|
|
101
|
+
if (typeof traverseFunc !== "function") {
|
|
79
102
|
traverseFunc = traverseFunc.default;
|
|
80
103
|
}
|
|
81
|
-
if (typeof traverseFunc !==
|
|
104
|
+
if (typeof traverseFunc !== "function") {
|
|
82
105
|
traverseFunc = traverseFunc.default;
|
|
83
106
|
}
|
|
84
107
|
traverseFunc(ast, {
|
|
108
|
+
// biome-ignore lint/suspicious/noExplicitAny: babel-traverse visitor `path` is a deeply-generic NodePath whose precise type depends on what handler you're inside; `any` is the documented practice for plugin code.
|
|
85
109
|
CallExpression(path) {
|
|
86
110
|
const callee = path.node.callee;
|
|
87
111
|
// Check if this is a requirement() call
|
|
88
|
-
if (t.isIdentifier(callee) && callee.name ===
|
|
112
|
+
if (t.isIdentifier(callee) && callee.name === "requirement") {
|
|
89
113
|
const args = path.node.arguments;
|
|
90
114
|
const loc = path.node.loc;
|
|
91
115
|
// Extract all string literal arguments (supports multi-requirement calls)
|
|
@@ -98,7 +122,7 @@ export async function findRequirementsInFile(filePath) {
|
|
|
98
122
|
line: loc.start.line,
|
|
99
123
|
column: loc.start.column + 1, // 1-indexed
|
|
100
124
|
requirementId,
|
|
101
|
-
context: code.split(
|
|
125
|
+
context: code.split("\n")[loc.start.line - 1] || "",
|
|
102
126
|
});
|
|
103
127
|
}
|
|
104
128
|
}
|
|
@@ -125,14 +149,36 @@ export async function findAllTestReferences(workspaceRoot) {
|
|
|
125
149
|
let output;
|
|
126
150
|
if (useRg) {
|
|
127
151
|
// Using execFile with array args to avoid shell injection
|
|
128
|
-
const { stdout } = await execFileAsync(
|
|
152
|
+
const { stdout } = await execFileAsync("rg", [
|
|
153
|
+
"-n",
|
|
154
|
+
"--column",
|
|
155
|
+
pattern,
|
|
156
|
+
"--type",
|
|
157
|
+
"ts",
|
|
158
|
+
"--type",
|
|
159
|
+
"js",
|
|
160
|
+
"--type",
|
|
161
|
+
"tsx",
|
|
162
|
+
"--type",
|
|
163
|
+
"jsx",
|
|
164
|
+
workspaceRoot,
|
|
165
|
+
], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // ripgrep returns non-zero on no matches
|
|
129
166
|
output = stdout;
|
|
130
167
|
}
|
|
131
168
|
else {
|
|
132
|
-
const { stdout } = await execFileAsync(
|
|
169
|
+
const { stdout } = await execFileAsync("grep", [
|
|
170
|
+
"-rn",
|
|
171
|
+
"--include=*.ts",
|
|
172
|
+
"--include=*.tsx",
|
|
173
|
+
"--include=*.js",
|
|
174
|
+
"--include=*.jsx",
|
|
175
|
+
"-E",
|
|
176
|
+
pattern,
|
|
177
|
+
workspaceRoot,
|
|
178
|
+
], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // grep returns non-zero on no matches
|
|
133
179
|
output = stdout;
|
|
134
180
|
}
|
|
135
|
-
return parseGrepOutput(output,
|
|
181
|
+
return parseGrepOutput(output, "", useRg);
|
|
136
182
|
}
|
|
137
183
|
catch {
|
|
138
184
|
return [];
|
|
@@ -143,7 +189,7 @@ export async function findAllTestReferences(workspaceRoot) {
|
|
|
143
189
|
*/
|
|
144
190
|
function parseGrepOutput(output, expectedId, isRipgrep, singleFilePath) {
|
|
145
191
|
const results = [];
|
|
146
|
-
const lines = output.trim().split(
|
|
192
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
147
193
|
for (const line of lines) {
|
|
148
194
|
let match;
|
|
149
195
|
let file;
|
|
@@ -200,41 +246,43 @@ function parseGrepOutput(output, expectedId, isRipgrep, singleFilePath) {
|
|
|
200
246
|
* Uses AST parsing instead of grep for reliability across environments
|
|
201
247
|
*/
|
|
202
248
|
export async function getReferencedRequirementIds(workspaceRoot) {
|
|
203
|
-
const { glob } = await import(
|
|
204
|
-
const { parse } = await import(
|
|
205
|
-
const traverse = await import(
|
|
206
|
-
const t = await import(
|
|
207
|
-
const fs = await import(
|
|
249
|
+
const { glob } = await import("glob");
|
|
250
|
+
const { parse } = await import("@babel/parser");
|
|
251
|
+
const traverse = await import("@babel/traverse");
|
|
252
|
+
const t = await import("@babel/types");
|
|
253
|
+
const fs = await import("node:fs");
|
|
208
254
|
// Find all test files using glob (same pattern as get_requirement tool)
|
|
209
|
-
const testFiles = await glob(
|
|
255
|
+
const testFiles = await glob("**/*.{test,spec}.{js,jsx,ts,tsx}", {
|
|
210
256
|
cwd: workspaceRoot,
|
|
211
257
|
absolute: true,
|
|
212
|
-
ignore: [
|
|
258
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
|
|
213
259
|
});
|
|
214
260
|
const allRequirementIds = [];
|
|
215
261
|
// For each test file, parse AST and extract requirement IDs
|
|
216
262
|
for (const file of testFiles) {
|
|
217
263
|
try {
|
|
218
|
-
const code = fs.readFileSync(file,
|
|
264
|
+
const code = fs.readFileSync(file, "utf-8");
|
|
219
265
|
const ast = parse(code, {
|
|
220
|
-
sourceType:
|
|
221
|
-
plugins: [
|
|
266
|
+
sourceType: "module",
|
|
267
|
+
plugins: ["typescript", "jsx"],
|
|
222
268
|
errorRecovery: true,
|
|
223
269
|
});
|
|
224
270
|
// Handle both ES module and CommonJS exports for traverse
|
|
225
271
|
// @babel/traverse can be imported as: traverse.default.default in compiled code
|
|
272
|
+
// biome-ignore lint/suspicious/noExplicitAny: see earlier comment on babel/traverse CJS/ESM interop
|
|
226
273
|
let traverseFunc = traverse;
|
|
227
|
-
if (typeof traverseFunc !==
|
|
274
|
+
if (typeof traverseFunc !== "function") {
|
|
228
275
|
traverseFunc = traverseFunc.default;
|
|
229
276
|
}
|
|
230
|
-
if (typeof traverseFunc !==
|
|
277
|
+
if (typeof traverseFunc !== "function") {
|
|
231
278
|
traverseFunc = traverseFunc.default;
|
|
232
279
|
}
|
|
233
280
|
traverseFunc(ast, {
|
|
281
|
+
// biome-ignore lint/suspicious/noExplicitAny: babel-traverse visitor `path` is a deeply-generic NodePath whose precise type depends on what handler you're inside; `any` is the documented practice for plugin code.
|
|
234
282
|
CallExpression(path) {
|
|
235
283
|
const callee = path.node.callee;
|
|
236
284
|
// Check if this is a requirement() call
|
|
237
|
-
if (t.isIdentifier(callee) && callee.name ===
|
|
285
|
+
if (t.isIdentifier(callee) && callee.name === "requirement") {
|
|
238
286
|
const args = path.node.arguments;
|
|
239
287
|
// Extract all string literal arguments (supports multi-requirement calls)
|
|
240
288
|
for (const arg of args) {
|
|
@@ -246,14 +294,11 @@ export async function getReferencedRequirementIds(workspaceRoot) {
|
|
|
246
294
|
},
|
|
247
295
|
});
|
|
248
296
|
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
// Skip unparseable files
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
297
|
+
catch (error) { }
|
|
253
298
|
}
|
|
254
299
|
// Extract root IDs by removing the path suffix (everything after the first dot)
|
|
255
|
-
const rootIds = allRequirementIds.map(id => {
|
|
256
|
-
const dotIndex = id.indexOf(
|
|
300
|
+
const rootIds = allRequirementIds.map((id) => {
|
|
301
|
+
const dotIndex = id.indexOf(".");
|
|
257
302
|
return dotIndex > 0 ? id.substring(0, dotIndex) : id;
|
|
258
303
|
});
|
|
259
304
|
return new Set(rootIds);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - create_requirement_document: Generate requirements template with format guidance
|
|
6
6
|
* - validate_requirements: Validate requirements file syntax offline
|
|
7
7
|
*/
|
|
8
|
-
import type { HandlerContext, ToolResponse } from
|
|
8
|
+
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* Arguments for create_requirement_document tool
|
|
11
11
|
*/
|