@popoverai/dotrequirements 0.24.3 → 0.26.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 +7 -8
- package/dist/cli.js +8 -1
- package/dist/codebase-to-spec/dispatch.d.ts +60 -14
- package/dist/codebase-to-spec/dispatch.js +381 -15
- package/dist/codebase-to-spec/pack.d.ts +7 -0
- package/dist/codebase-to-spec/pack.js +29 -8
- package/dist/codebase-to-spec/present.d.ts +9 -0
- package/dist/codebase-to-spec/present.js +23 -2
- package/dist/codebase-to-spec/prompts/editor.d.ts +1 -1
- package/dist/codebase-to-spec/prompts/editor.js +1 -1
- package/dist/codebase-to-spec/prompts/specifier.d.ts +1 -1
- package/dist/codebase-to-spec/prompts/specifier.js +3 -2
- package/dist/codebase-to-spec/schemas.d.ts +153 -0
- package/dist/codebase-to-spec/schemas.js +111 -0
- package/dist/codebase-to-spec/skill-install.d.ts +42 -29
- package/dist/codebase-to-spec/skill-install.js +122 -112
- package/dist/codebase-to-spec/version-check.d.ts +31 -0
- package/dist/codebase-to-spec/version-check.js +56 -0
- package/dist/commands/ai-setup.d.ts +12 -1
- package/dist/commands/ai-setup.js +65 -33
- package/dist/commands/codebase-to-spec/dispatch-context.d.ts +2 -5
- package/dist/commands/codebase-to-spec/dispatch-context.js +2 -5
- package/dist/commands/codebase-to-spec/dispatch-editor.d.ts +0 -1
- package/dist/commands/codebase-to-spec/dispatch-editor.js +0 -1
- package/dist/commands/codebase-to-spec/dispatch-planner.d.ts +0 -1
- package/dist/commands/codebase-to-spec/dispatch-planner.js +0 -1
- package/dist/commands/codebase-to-spec/dispatch-spec.d.ts +3 -6
- package/dist/commands/codebase-to-spec/dispatch-spec.js +3 -6
- package/dist/commands/codebase-to-spec/index.js +3 -2
- package/dist/commands/codebase-to-spec/pack.d.ts +9 -0
- package/dist/commands/codebase-to-spec/pack.js +23 -3
- package/dist/commands/codebase-to-spec/skill-install.js +2 -9
- package/dist/commands/init.js +6 -1
- package/dist/commands/link-resolution.d.ts +79 -0
- package/dist/commands/link-resolution.js +141 -0
- package/dist/commands/link.d.ts +14 -4
- package/dist/commands/link.js +369 -16
- package/dist/commands/pull.js +19 -2
- package/dist/commands/push.js +36 -2
- package/dist/convex.d.ts +5 -3
- package/dist/convex.js +5 -3
- package/dist/harness/cache.d.ts +0 -14
- package/dist/harness/cache.js +1 -41
- package/dist/harness/finalize.js +2 -2
- package/dist/harness/prepare.js +1 -3
- package/dist/harness/requirementsLoader.d.ts +3 -3
- package/dist/harness/requirementsLoader.js +13 -8
- package/dist/mcp/handlers/authoring.d.ts +5 -5
- package/dist/mcp/handlers/authoring.js +9 -9
- package/dist/mcp/handlers/push.d.ts +2 -2
- package/dist/mcp/handlers/push.js +36 -3
- package/dist/mcp/handlers/review.d.ts +4 -4
- package/dist/mcp/handlers/review.js +4 -4
- package/dist/mcp/handlers/search.d.ts +1 -1
- package/dist/mcp/handlers/search.js +1 -1
- package/dist/mcp/index.js +29 -0
- package/dist/push/core.d.ts +18 -0
- package/dist/push/core.js +70 -3
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.js +1 -1
- package/dist/schema/parser-core.js +5 -1
- package/dist/schema/parser.js +5 -1
- package/dist/schema/run-marker.d.ts +38 -0
- package/dist/schema/run-marker.js +138 -0
- package/dist/schema/schemas.d.ts +12 -0
- package/dist/schema/schemas.js +1 -0
- package/dist/templates/agents/cts-worker.md +3 -3
- package/dist/templates/skills/codebase-to-spec/SKILL.md +56 -158
- package/dist/templates/workflows/specify-codebase.js +374 -0
- package/dist/utils/own-package.d.ts +10 -0
- package/dist/utils/own-package.js +13 -0
- package/dist/utils/project-selector.d.ts +5 -0
- package/dist/utils/project-selector.js +4 -0
- package/package.json +3 -3
- package/dist/templates/hooks/cts-worker-persona.sh +0 -76
package/dist/commands/push.js
CHANGED
|
@@ -100,16 +100,50 @@ export async function pushCommand(file, options) {
|
|
|
100
100
|
console.log(` ✓ Updated: ${fileName}`);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
// IMPORT-3: marker failures are loud but never fatal
|
|
104
|
+
if (result.importWarnings.length > 0) {
|
|
105
|
+
console.log();
|
|
106
|
+
for (const { fileName, status } of result.importWarnings) {
|
|
107
|
+
if (status === "already_used") {
|
|
108
|
+
console.log(`⚠ ${fileName}: this file's import marker was already used, so its requirements were saved as natively authored (they count toward your plan's requirement limit). Re-running codebase-to-spec produces a fresh marker.`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(`⚠ ${fileName}: this file's import marker was not recognized — your CLI may need updating. Its requirements were saved as natively authored (they count toward your plan's requirement limit).`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// SYNC-FAIL-1: a failed push must not masquerade as success — agents and
|
|
116
|
+
// scripts drive this command and rely on the exit code to tell the truth
|
|
117
|
+
const failed = result.errors.length;
|
|
118
|
+
const syncedCount = result.created + result.updated;
|
|
119
|
+
if (failed > 0) {
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
if (syncedCount === 0) {
|
|
122
|
+
console.log(`\n✗ Push failed: ${failed} document(s) could not be saved.`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(`\n⚠ Push incomplete: ${syncedCount} document(s) synced, ${failed} failed.`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.log(`\n✓ Push complete!`);
|
|
130
|
+
}
|
|
104
131
|
if (result.created > 0) {
|
|
105
132
|
console.log(` Created: ${result.created} document(s)`);
|
|
106
133
|
}
|
|
107
134
|
if (result.updated > 0) {
|
|
108
135
|
console.log(` Updated: ${result.updated} document(s)`);
|
|
109
136
|
}
|
|
110
|
-
if (
|
|
137
|
+
if (failed === 0 && syncedCount === 0) {
|
|
111
138
|
console.log(" No documents were pushed.");
|
|
112
139
|
}
|
|
140
|
+
// SYNC-LAND-1: land the user on their spec, not at a dead end
|
|
141
|
+
if (result.synced.length > 0) {
|
|
142
|
+
console.log("\nReview in dot•requirements:");
|
|
143
|
+
for (const doc of result.synced) {
|
|
144
|
+
console.log(` ${doc.fileName} → ${doc.url}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
113
147
|
}
|
|
114
148
|
/**
|
|
115
149
|
* Display the dry run summary.
|
package/dist/convex.d.ts
CHANGED
|
@@ -34,13 +34,11 @@ export declare const api: {
|
|
|
34
34
|
queries: {
|
|
35
35
|
listByProject: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
36
36
|
};
|
|
37
|
-
mutations: {
|
|
38
|
-
bulkUpdate: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
39
|
-
};
|
|
40
37
|
};
|
|
41
38
|
projects: {
|
|
42
39
|
mutations: {
|
|
43
40
|
create: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
41
|
+
update: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
44
42
|
};
|
|
45
43
|
};
|
|
46
44
|
teams: {
|
|
@@ -55,6 +53,9 @@ export declare const api: {
|
|
|
55
53
|
queries: {
|
|
56
54
|
getInviteInfo: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
57
55
|
};
|
|
56
|
+
mutations: {
|
|
57
|
+
createInvite: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
58
|
+
};
|
|
58
59
|
};
|
|
59
60
|
polar: {
|
|
60
61
|
acceptTeamInvite: import("convex/server").FunctionReference<"action", "public", any, any, string | undefined>;
|
|
@@ -67,6 +68,7 @@ export declare const api: {
|
|
|
67
68
|
mutations: {
|
|
68
69
|
ensureOwnSecret: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
69
70
|
revokeOwnSecret: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
71
|
+
ensureShareToken: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
70
72
|
};
|
|
71
73
|
};
|
|
72
74
|
lib: {
|
package/dist/convex.js
CHANGED
|
@@ -40,13 +40,11 @@ export const api = {
|
|
|
40
40
|
queries: {
|
|
41
41
|
listByProject: query("requirements/queries:listByProject"),
|
|
42
42
|
},
|
|
43
|
-
mutations: {
|
|
44
|
-
bulkUpdate: mutation("requirements/mutations:bulkUpdate"),
|
|
45
|
-
},
|
|
46
43
|
},
|
|
47
44
|
projects: {
|
|
48
45
|
mutations: {
|
|
49
46
|
create: mutation("projects/mutations:create"),
|
|
47
|
+
update: mutation("projects/mutations:update"),
|
|
50
48
|
},
|
|
51
49
|
},
|
|
52
50
|
teams: {
|
|
@@ -61,6 +59,9 @@ export const api = {
|
|
|
61
59
|
queries: {
|
|
62
60
|
getInviteInfo: query("teamInvites/queries:getInviteInfo"),
|
|
63
61
|
},
|
|
62
|
+
mutations: {
|
|
63
|
+
createInvite: mutation("teamInvites/mutations:createInvite"),
|
|
64
|
+
},
|
|
64
65
|
},
|
|
65
66
|
polar: {
|
|
66
67
|
acceptTeamInvite: action("polar:acceptTeamInvite"),
|
|
@@ -73,6 +74,7 @@ export const api = {
|
|
|
73
74
|
mutations: {
|
|
74
75
|
ensureOwnSecret: mutation("projectSecrets/mutations:ensureOwnSecret"),
|
|
75
76
|
revokeOwnSecret: mutation("projectSecrets/mutations:revokeOwnSecret"),
|
|
77
|
+
ensureShareToken: mutation("projectSecrets/mutations:ensureShareToken"),
|
|
76
78
|
},
|
|
77
79
|
},
|
|
78
80
|
lib: {
|
package/dist/harness/cache.d.ts
CHANGED
|
@@ -94,20 +94,6 @@ export declare function getTestRunId(requirementsDir: string): string | null;
|
|
|
94
94
|
* Clean up the test run ID file
|
|
95
95
|
*/
|
|
96
96
|
export declare function cleanupTestRunId(requirementsDir: string): void;
|
|
97
|
-
/**
|
|
98
|
-
* Write the project root to cache (for cross-process persistence).
|
|
99
|
-
* This allows test workers to find the lookup cache even after cwd changes.
|
|
100
|
-
*/
|
|
101
|
-
export declare function writeProjectRoot(requirementsDir: string, projectRoot: string): void;
|
|
102
|
-
/**
|
|
103
|
-
* Read the project root from cache, returning null if not found.
|
|
104
|
-
*/
|
|
105
|
-
export declare function readProjectRoot(requirementsDir: string): string | null;
|
|
106
|
-
/**
|
|
107
|
-
* Try to find the project root from any known .requirements cache directory.
|
|
108
|
-
* Walks up from cwd looking for .requirements/.cache/project-root file.
|
|
109
|
-
*/
|
|
110
|
-
export declare function findCachedProjectRoot(startDir?: string): string | null;
|
|
111
97
|
/**
|
|
112
98
|
* Clear tracking data for a new test run
|
|
113
99
|
*/
|
package/dist/harness/cache.js
CHANGED
|
@@ -14,7 +14,6 @@ const CACHE_DIR = ".cache";
|
|
|
14
14
|
const LOOKUP_FILE = "lookup.json";
|
|
15
15
|
const TRACKING_FILE = "tracking.jsonl";
|
|
16
16
|
const COVERAGE_FILE = "coverage.json";
|
|
17
|
-
const PROJECT_ROOT_FILE = "project-root";
|
|
18
17
|
// Test run ID file (outside cache, in .requirements/)
|
|
19
18
|
const TEST_RUN_ID_FILE = ".test-run-id";
|
|
20
19
|
/**
|
|
@@ -70,7 +69,7 @@ export function writeLookupCache(requirementsDir, requirements) {
|
|
|
70
69
|
label: node.label,
|
|
71
70
|
content: node.content,
|
|
72
71
|
};
|
|
73
|
-
// Always add the numeric path (e.g. "AUTH-LOGIN.0")
|
|
72
|
+
// Always add the numeric path (e.g. "AUTH-LOGIN-1.0")
|
|
74
73
|
lookup.requirements[node.id] = entry;
|
|
75
74
|
// Add label path as an alias if it differs from the numeric path
|
|
76
75
|
if (labelPath && labelPath !== node.id) {
|
|
@@ -174,45 +173,6 @@ export function cleanupTestRunId(requirementsDir) {
|
|
|
174
173
|
fs.unlinkSync(testRunIdPath);
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Write the project root to cache (for cross-process persistence).
|
|
179
|
-
* This allows test workers to find the lookup cache even after cwd changes.
|
|
180
|
-
*/
|
|
181
|
-
export function writeProjectRoot(requirementsDir, projectRoot) {
|
|
182
|
-
const cacheDir = getCacheDir(requirementsDir, true);
|
|
183
|
-
const projectRootPath = path.join(cacheDir, PROJECT_ROOT_FILE);
|
|
184
|
-
fs.writeFileSync(projectRootPath, projectRoot);
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Read the project root from cache, returning null if not found.
|
|
188
|
-
*/
|
|
189
|
-
export function readProjectRoot(requirementsDir) {
|
|
190
|
-
const cacheDir = getCacheDir(requirementsDir);
|
|
191
|
-
const projectRootPath = path.join(cacheDir, PROJECT_ROOT_FILE);
|
|
192
|
-
if (!fs.existsSync(projectRootPath)) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
return fs.readFileSync(projectRootPath, "utf-8").trim();
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Try to find the project root from any known .requirements cache directory.
|
|
199
|
-
* Walks up from cwd looking for .requirements/.cache/project-root file.
|
|
200
|
-
*/
|
|
201
|
-
export function findCachedProjectRoot(startDir = process.cwd()) {
|
|
202
|
-
let currentDir = path.resolve(startDir);
|
|
203
|
-
while (true) {
|
|
204
|
-
const requirementsDir = path.join(currentDir, ".requirements");
|
|
205
|
-
const projectRootPath = path.join(requirementsDir, CACHE_DIR, PROJECT_ROOT_FILE);
|
|
206
|
-
if (fs.existsSync(projectRootPath)) {
|
|
207
|
-
return fs.readFileSync(projectRootPath, "utf-8").trim();
|
|
208
|
-
}
|
|
209
|
-
const parentDir = path.dirname(currentDir);
|
|
210
|
-
if (parentDir === currentDir) {
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
currentDir = parentDir;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
176
|
/**
|
|
217
177
|
* Clear tracking data for a new test run
|
|
218
178
|
*/
|
package/dist/harness/finalize.js
CHANGED
|
@@ -333,8 +333,8 @@ export async function finalize(options = {}) {
|
|
|
333
333
|
// Read lookup cache for report
|
|
334
334
|
const lookup = readLookupCache(requirementsDir);
|
|
335
335
|
// Normalize alias keys to canonical numeric keys.
|
|
336
|
-
// Non-JS consumers may write label paths (e.g. "AUTH-LOGIN.given") to tracking.jsonl.
|
|
337
|
-
// We resolve those to their canonical numeric key (e.g. "AUTH-LOGIN.0") so that
|
|
336
|
+
// Non-JS consumers may write label paths (e.g. "AUTH-LOGIN-1.given") to tracking.jsonl.
|
|
337
|
+
// We resolve those to their canonical numeric key (e.g. "AUTH-LOGIN-1.0") so that
|
|
338
338
|
// coverage counting, display, and cloud reporting all use consistent keys.
|
|
339
339
|
if (lookup) {
|
|
340
340
|
for (const [key, trackingEntries] of Array.from(aggregated.entries())) {
|
package/dist/harness/prepare.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { findRequirementsFilesSync } from "../requirements/index.js";
|
|
12
12
|
import { parseRequirementsFromFile, } from "../schema/index.js";
|
|
13
13
|
import { findProjectRoot } from "../utils/project-settings.js";
|
|
14
|
-
import { clearTrackingFile, findRequirementsDir, initTestRunId, writeLookupCache,
|
|
14
|
+
import { clearTrackingFile, findRequirementsDir, initTestRunId, writeLookupCache, } from "./cache.js";
|
|
15
15
|
/**
|
|
16
16
|
* Prepare the test harness for a test run.
|
|
17
17
|
*
|
|
@@ -54,8 +54,6 @@ export function prepare(options = {}) {
|
|
|
54
54
|
}
|
|
55
55
|
// HARNESS-PREPARE-1: Write lookup cache for fast resolution
|
|
56
56
|
writeLookupCache(requirementsDir, allRequirements);
|
|
57
|
-
// Write project root to cache (for cross-process persistence in test workers)
|
|
58
|
-
writeProjectRoot(requirementsDir, projectRoot);
|
|
59
57
|
// HARNESS-PREPARE-3.0: Initialize cache directory (done by writeLookupCache)
|
|
60
58
|
// HARNESS-PREPARE-3.1: Clear any previous tracking data
|
|
61
59
|
clearTrackingFile(requirementsDir);
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Project root discovery (in priority order):
|
|
6
6
|
* 1. Explicit projectRoot option passed to loadRequirements()
|
|
7
|
-
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
|
|
8
|
-
* 3.
|
|
7
|
+
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable (set by prepare())
|
|
8
|
+
* 3. Walk up from the working directory to the nearest .requirements/ folder
|
|
9
9
|
*/
|
|
10
10
|
import { type RequirementNode } from "../schema/index.js";
|
|
11
11
|
import type { Requirement } from "./types.js";
|
|
12
12
|
export declare const loadedRequirements: Map<string, RequirementNode>;
|
|
13
13
|
export interface LoadOptions {
|
|
14
|
-
/** Explicit project root path. Takes precedence over env var and
|
|
14
|
+
/** Explicit project root path. Takes precedence over env var and walk-up discovery. */
|
|
15
15
|
projectRoot?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Project root discovery (in priority order):
|
|
6
6
|
* 1. Explicit projectRoot option passed to loadRequirements()
|
|
7
|
-
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
|
|
8
|
-
* 3.
|
|
7
|
+
* 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable (set by prepare())
|
|
8
|
+
* 3. Walk up from the working directory to the nearest .requirements/ folder
|
|
9
9
|
*/
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { findRequirementsFilesSync } from "../requirements/index.js";
|
|
13
13
|
import { parseRequirementsFromFile, resolveRequirementPath, } from "../schema/index.js";
|
|
14
|
-
import {
|
|
14
|
+
import { findProjectRoot as findProjectRootByWalkUp, 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
|
|
@@ -22,7 +22,12 @@ let warnedAboutFallback = false;
|
|
|
22
22
|
* Find the project root using the priority order:
|
|
23
23
|
* 1. Explicit projectRoot option
|
|
24
24
|
* 2. DOTREQUIREMENTS_PROJECT_ROOT env var
|
|
25
|
-
* 3.
|
|
25
|
+
* 3. Walk up from cwd to the nearest .requirements/ folder
|
|
26
|
+
*
|
|
27
|
+
* The walk-up means the nearest enclosing project wins: tests running in a
|
|
28
|
+
* worktree nested inside another checkout load that worktree's requirements,
|
|
29
|
+
* not the outer checkout's (HARNESS-REQUIREMENT-4.3). It also matches how
|
|
30
|
+
* tracking.ts discovers the requirements directory.
|
|
26
31
|
*/
|
|
27
32
|
function findProjectRoot(options = {}) {
|
|
28
33
|
// Priority 1: Explicit option
|
|
@@ -33,8 +38,8 @@ function findProjectRoot(options = {}) {
|
|
|
33
38
|
if (process.env.DOTREQUIREMENTS_PROJECT_ROOT) {
|
|
34
39
|
return process.env.DOTREQUIREMENTS_PROJECT_ROOT;
|
|
35
40
|
}
|
|
36
|
-
// Priority 3:
|
|
37
|
-
return
|
|
41
|
+
// Priority 3: Nearest .requirements/ folder (HARNESS-REQUIREMENT-4.2)
|
|
42
|
+
return findProjectRootByWalkUp(process.cwd());
|
|
38
43
|
}
|
|
39
44
|
/**
|
|
40
45
|
* Try to load requirements from lookup cache (fast path).
|
|
@@ -97,8 +102,8 @@ export function loadRequirements(options = {}) {
|
|
|
97
102
|
// Find project root
|
|
98
103
|
const projectRoot = findProjectRoot(options);
|
|
99
104
|
if (!projectRoot) {
|
|
100
|
-
throw new Error(
|
|
101
|
-
"
|
|
105
|
+
throw new Error(`Could not find project root: no .requirements/ directory in ${process.cwd()} or any parent directory. ` +
|
|
106
|
+
"Create one with `dotrequirements init`, or set the DOTREQUIREMENTS_PROJECT_ROOT environment variable.");
|
|
102
107
|
}
|
|
103
108
|
// Try cache first (fast path)
|
|
104
109
|
if (tryLoadFromCache(projectRoot)) {
|
|
@@ -23,7 +23,7 @@ export interface ValidateRequirementsArgs {
|
|
|
23
23
|
*
|
|
24
24
|
* Requirements covered:
|
|
25
25
|
* - MCP-AUTHOR-1.0: The template includes format guidance with code block examples
|
|
26
|
-
* - MCP-AUTHOR-1.1: The template includes guidance
|
|
26
|
+
* - MCP-AUTHOR-1.1: The template includes style guidance (concrete examples, concise prose, testable conditions)
|
|
27
27
|
* - MCP-AUTHOR-1.2: When the project has requirementsStyleContext configured, it is included in the template
|
|
28
28
|
* - MCP-AUTHOR-1.3: When cloud credentials are unavailable, the template works without the custom context
|
|
29
29
|
*/
|
|
@@ -32,10 +32,10 @@ export declare function handleCreateRequirementDocument(args: CreateRequirementD
|
|
|
32
32
|
* Handler for validate_requirements tool
|
|
33
33
|
*
|
|
34
34
|
* Requirements covered:
|
|
35
|
-
* - MCP-AUTHOR-2.
|
|
36
|
-
* - MCP-AUTHOR-2.
|
|
37
|
-
* - MCP-AUTHOR-2.
|
|
38
|
-
* - MCP-AUTHOR-2.
|
|
35
|
+
* - MCP-AUTHOR-2.1: When the file has valid syntax, the response confirms validation passed
|
|
36
|
+
* - MCP-AUTHOR-2.2: When the file has syntax errors, the response lists each error with location
|
|
37
|
+
* - MCP-AUTHOR-2.3: Validation does not require network access or cloud credentials
|
|
38
|
+
* - MCP-AUTHOR-2.4: When the file does not exist, an error is returned
|
|
39
39
|
*/
|
|
40
40
|
export declare function handleValidateRequirements(args: ValidateRequirementsArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
41
41
|
//# sourceMappingURL=authoring.d.ts.map
|
|
@@ -17,7 +17,7 @@ import { errorResponse, textResponse } from "./types.js";
|
|
|
17
17
|
*
|
|
18
18
|
* Requirements covered:
|
|
19
19
|
* - MCP-AUTHOR-1.0: The template includes format guidance with code block examples
|
|
20
|
-
* - MCP-AUTHOR-1.1: The template includes guidance
|
|
20
|
+
* - MCP-AUTHOR-1.1: The template includes style guidance (concrete examples, concise prose, testable conditions)
|
|
21
21
|
* - MCP-AUTHOR-1.2: When the project has requirementsStyleContext configured, it is included in the template
|
|
22
22
|
* - MCP-AUTHOR-1.3: When cloud credentials are unavailable, the template works without the custom context
|
|
23
23
|
*/
|
|
@@ -53,21 +53,21 @@ export async function handleCreateRequirementDocument(args, context) {
|
|
|
53
53
|
* Handler for validate_requirements tool
|
|
54
54
|
*
|
|
55
55
|
* Requirements covered:
|
|
56
|
-
* - MCP-AUTHOR-2.
|
|
57
|
-
* - MCP-AUTHOR-2.
|
|
58
|
-
* - MCP-AUTHOR-2.
|
|
59
|
-
* - MCP-AUTHOR-2.
|
|
56
|
+
* - MCP-AUTHOR-2.1: When the file has valid syntax, the response confirms validation passed
|
|
57
|
+
* - MCP-AUTHOR-2.2: When the file has syntax errors, the response lists each error with location
|
|
58
|
+
* - MCP-AUTHOR-2.3: Validation does not require network access or cloud credentials
|
|
59
|
+
* - MCP-AUTHOR-2.4: When the file does not exist, an error is returned
|
|
60
60
|
*/
|
|
61
61
|
export async function handleValidateRequirements(args, context) {
|
|
62
62
|
const { filePath } = args;
|
|
63
|
-
// MCP-AUTHOR-2.
|
|
63
|
+
// MCP-AUTHOR-2.3: Validation works offline - just use workspaceRoot
|
|
64
64
|
const fullPath = resolve(context.workspaceRoot, filePath);
|
|
65
|
-
// MCP-AUTHOR-2.
|
|
65
|
+
// MCP-AUTHOR-2.4: Check if file exists
|
|
66
66
|
if (!existsSync(fullPath)) {
|
|
67
67
|
return errorResponse(`File not found: ${filePath}`);
|
|
68
68
|
}
|
|
69
69
|
try {
|
|
70
|
-
// MCP-AUTHOR-2.
|
|
70
|
+
// MCP-AUTHOR-2.1 & MCP-AUTHOR-2.2: Parse and validate
|
|
71
71
|
const parsed = parseRequirementsFromFile(fullPath);
|
|
72
72
|
const reqCount = Object.keys(parsed.requirements).length;
|
|
73
73
|
// Check push readiness
|
|
@@ -89,7 +89,7 @@ export async function handleValidateRequirements(args, context) {
|
|
|
89
89
|
return textResponse(`${headline}${pushDetails}\n\n**Metadata:**\n- Version: ${parsed.metadata.version ?? "(none)"}\n- Document: ${parsed.metadata.document?.title || "(none)"}\n- Document ID: ${parsed.metadata.document?.id || "(none)"}\n\n**Requirements:** ${reqCount} root requirement(s) found`);
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
|
-
// MCP-AUTHOR-2.
|
|
92
|
+
// MCP-AUTHOR-2.2: Syntax errors are returned with details
|
|
93
93
|
return {
|
|
94
94
|
content: [
|
|
95
95
|
{
|
|
@@ -17,10 +17,10 @@ export interface PushRequirementsArgs {
|
|
|
17
17
|
* Handler for push_requirements tool
|
|
18
18
|
*
|
|
19
19
|
* Requirements covered:
|
|
20
|
-
* - MCP-PUSH-1.0: When
|
|
20
|
+
* - MCP-PUSH-1.0: When not confirmed, no changes are made and a preview of creates/updates is returned
|
|
21
21
|
* - MCP-PUSH-1.1: When confirmed is true, the push executes and returns success message
|
|
22
22
|
* - MCP-PUSH-1.2: When credentials are missing or invalid, an error explains how to authenticate
|
|
23
|
-
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed
|
|
23
|
+
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed (MCP-PUSH-1.3.0: missing file is an error)
|
|
24
24
|
*/
|
|
25
25
|
export declare function handlePushRequirements(args: PushRequirementsArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
26
26
|
//# sourceMappingURL=push.d.ts.map
|
|
@@ -14,10 +14,10 @@ import { errorResponse, textResponse } from "./types.js";
|
|
|
14
14
|
* Handler for push_requirements tool
|
|
15
15
|
*
|
|
16
16
|
* Requirements covered:
|
|
17
|
-
* - MCP-PUSH-1.0: When
|
|
17
|
+
* - MCP-PUSH-1.0: When not confirmed, no changes are made and a preview of creates/updates is returned
|
|
18
18
|
* - MCP-PUSH-1.1: When confirmed is true, the push executes and returns success message
|
|
19
19
|
* - MCP-PUSH-1.2: When credentials are missing or invalid, an error explains how to authenticate
|
|
20
|
-
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed
|
|
20
|
+
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed (MCP-PUSH-1.3.0: missing file is an error)
|
|
21
21
|
*/
|
|
22
22
|
export async function handlePushRequirements(args, context) {
|
|
23
23
|
const { filePath, confirmed = false, projectId } = args;
|
|
@@ -123,19 +123,52 @@ export async function handlePushRequirements(args, context) {
|
|
|
123
123
|
// MCP-PUSH-1.1: When confirmed, execute push
|
|
124
124
|
try {
|
|
125
125
|
const result = await executePush(dryRunResult, credentials);
|
|
126
|
-
|
|
126
|
+
// SYNC-FAIL-1: a failed push must not masquerade as success
|
|
127
|
+
const failed = result.errors.length;
|
|
128
|
+
const syncedCount = result.created + result.updated;
|
|
129
|
+
let output;
|
|
130
|
+
if (failed > 0 && syncedCount === 0) {
|
|
131
|
+
output = `✗ Push failed: ${failed} document(s) could not be saved.\n\n`;
|
|
132
|
+
}
|
|
133
|
+
else if (failed > 0) {
|
|
134
|
+
output = `⚠ Push incomplete: ${syncedCount} document(s) synced, ${failed} failed.\n\n`;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
output = "✓ Push complete!\n\n";
|
|
138
|
+
}
|
|
127
139
|
if (result.created > 0) {
|
|
128
140
|
output += `**Created:** ${result.created} document(s)\n`;
|
|
129
141
|
}
|
|
130
142
|
if (result.updated > 0) {
|
|
131
143
|
output += `**Updated:** ${result.updated} document(s)\n`;
|
|
132
144
|
}
|
|
145
|
+
// IMPORT-3: marker failures are loud but never fatal
|
|
146
|
+
if (result.importWarnings.length > 0) {
|
|
147
|
+
output += `\n**Import marker warnings:**\n`;
|
|
148
|
+
for (const { fileName, status } of result.importWarnings) {
|
|
149
|
+
output +=
|
|
150
|
+
status === "already_used"
|
|
151
|
+
? `- ⚠ ${fileName}: import marker already used — requirements were saved as natively authored (they count toward the plan's requirement limit). Re-running codebase-to-spec produces a fresh marker.\n`
|
|
152
|
+
: `- ⚠ ${fileName}: import marker not recognized — the CLI may need updating. Requirements were saved as natively authored (they count toward the plan's requirement limit).\n`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
133
155
|
if (result.errors.length > 0) {
|
|
134
156
|
output += `\n**Errors:**\n`;
|
|
135
157
|
for (const { fileName, error } of result.errors) {
|
|
136
158
|
output += `- ${fileName}: ${error}\n`;
|
|
137
159
|
}
|
|
138
160
|
}
|
|
161
|
+
// SYNC-LAND-1: where each synced document lives in the web app
|
|
162
|
+
if (result.synced.length > 0) {
|
|
163
|
+
output += `\n**Review in dot•requirements:**\n`;
|
|
164
|
+
for (const doc of result.synced) {
|
|
165
|
+
output += `- ${doc.fileName}: ${doc.url}\n`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// SYNC-FAIL-1.2: total failure is an error result, not a success story
|
|
169
|
+
if (failed > 0 && syncedCount === 0) {
|
|
170
|
+
return { content: [{ type: "text", text: output }], isError: true };
|
|
171
|
+
}
|
|
139
172
|
return textResponse(output);
|
|
140
173
|
}
|
|
141
174
|
catch (error) {
|
|
@@ -25,7 +25,7 @@ export interface ReviewTestArgs {
|
|
|
25
25
|
* Handler for style_check tool
|
|
26
26
|
*
|
|
27
27
|
* Requirements covered:
|
|
28
|
-
* - MCP-REVIEW-1.0: For requirements files, feedback identifies vague language
|
|
28
|
+
* - MCP-REVIEW-1.0: For requirements files, feedback identifies clarity issues such as vague language and missing preconditions
|
|
29
29
|
* - MCP-REVIEW-1.1: For test files, feedback identifies incorrect requirement() usage and missing test coverage
|
|
30
30
|
* - MCP-REVIEW-1.2: Feedback is categorized by severity: must fix, should fix, could improve
|
|
31
31
|
* - MCP-REVIEW-1.3: When cloud credentials are unavailable, an error explains how to authenticate
|
|
@@ -38,9 +38,9 @@ export declare function handleStyleCheck(args: StyleCheckArgs, context: HandlerC
|
|
|
38
38
|
* Handler for review_test tool
|
|
39
39
|
*
|
|
40
40
|
* Requirements covered:
|
|
41
|
-
* - MCP-REVIEW-2.0:
|
|
42
|
-
* - MCP-REVIEW-2.1:
|
|
43
|
-
* - MCP-REVIEW-2.2:
|
|
41
|
+
* - MCP-REVIEW-2.0: Feedback identifies test setup that does not match requirement preconditions
|
|
42
|
+
* - MCP-REVIEW-2.1: Feedback identifies test actions that do not match requirement triggers
|
|
43
|
+
* - MCP-REVIEW-2.2: Feedback identifies test assertions that do not match requirement outcomes
|
|
44
44
|
* - MCP-REVIEW-2.3: Feedback identifies requirements without test coverage
|
|
45
45
|
* - MCP-REVIEW-2.4: Feedback identifies tests that reference non-existent requirements
|
|
46
46
|
* - MCP-REVIEW-2.5: When cloud credentials are unavailable, an error explains how to authenticate
|
|
@@ -26,7 +26,7 @@ const STYLE_CHECK_GUIDANCE = `
|
|
|
26
26
|
* Handler for style_check tool
|
|
27
27
|
*
|
|
28
28
|
* Requirements covered:
|
|
29
|
-
* - MCP-REVIEW-1.0: For requirements files, feedback identifies vague language
|
|
29
|
+
* - MCP-REVIEW-1.0: For requirements files, feedback identifies clarity issues such as vague language and missing preconditions
|
|
30
30
|
* - MCP-REVIEW-1.1: For test files, feedback identifies incorrect requirement() usage and missing test coverage
|
|
31
31
|
* - MCP-REVIEW-1.2: Feedback is categorized by severity: must fix, should fix, could improve
|
|
32
32
|
* - MCP-REVIEW-1.3: When cloud credentials are unavailable, an error explains how to authenticate
|
|
@@ -120,9 +120,9 @@ export async function handleStyleCheck(args, context, options) {
|
|
|
120
120
|
* Handler for review_test tool
|
|
121
121
|
*
|
|
122
122
|
* Requirements covered:
|
|
123
|
-
* - MCP-REVIEW-2.0:
|
|
124
|
-
* - MCP-REVIEW-2.1:
|
|
125
|
-
* - MCP-REVIEW-2.2:
|
|
123
|
+
* - MCP-REVIEW-2.0: Feedback identifies test setup that does not match requirement preconditions
|
|
124
|
+
* - MCP-REVIEW-2.1: Feedback identifies test actions that do not match requirement triggers
|
|
125
|
+
* - MCP-REVIEW-2.2: Feedback identifies test assertions that do not match requirement outcomes
|
|
126
126
|
* - MCP-REVIEW-2.3: Feedback identifies requirements without test coverage
|
|
127
127
|
* - MCP-REVIEW-2.4: Feedback identifies tests that reference non-existent requirements
|
|
128
128
|
* - MCP-REVIEW-2.5: When cloud credentials are unavailable, an error explains how to authenticate
|
|
@@ -23,7 +23,7 @@ export interface SearchRequirementsArgs {
|
|
|
23
23
|
* - MCP-SEARCH-1.3: useRegex interprets query as case-insensitive regex
|
|
24
24
|
* - MCP-SEARCH-1.4: Invalid regex returns error
|
|
25
25
|
* - MCP-SEARCH-1.5: No matches returns informative message
|
|
26
|
-
* - MCP-SEARCH-1.6: Nested match returns root requirement
|
|
26
|
+
* - MCP-SEARCH-1.6: Nested match returns the full root requirement tree
|
|
27
27
|
* - MCP-SEARCH-1.7: Results include full tree as code block
|
|
28
28
|
*/
|
|
29
29
|
export declare function handleSearchRequirements(args: SearchRequirementsArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
@@ -16,7 +16,7 @@ import { errorResponse, textResponse } from "./types.js";
|
|
|
16
16
|
* - MCP-SEARCH-1.3: useRegex interprets query as case-insensitive regex
|
|
17
17
|
* - MCP-SEARCH-1.4: Invalid regex returns error
|
|
18
18
|
* - MCP-SEARCH-1.5: No matches returns informative message
|
|
19
|
-
* - MCP-SEARCH-1.6: Nested match returns root requirement
|
|
19
|
+
* - MCP-SEARCH-1.6: Nested match returns the full root requirement tree
|
|
20
20
|
* - MCP-SEARCH-1.7: Results include full tree as code block
|
|
21
21
|
*/
|
|
22
22
|
export async function handleSearchRequirements(args, context) {
|
package/dist/mcp/index.js
CHANGED
|
@@ -523,11 +523,40 @@ After writing tests:
|
|
|
523
523
|
}
|
|
524
524
|
throw new Error(`Unknown prompt: ${name}`);
|
|
525
525
|
});
|
|
526
|
+
/**
|
|
527
|
+
* MCP-ARGS-1: find required arguments (per the tool's inputSchema) that are
|
|
528
|
+
* missing from a call, so we can fail with a self-explaining error before the
|
|
529
|
+
* handler runs — a missing argument must never surface as an internal crash.
|
|
530
|
+
*/
|
|
531
|
+
function findMissingRequiredArgs(toolName, args) {
|
|
532
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
533
|
+
if (!tool)
|
|
534
|
+
return undefined; // unknown tools get their own error downstream
|
|
535
|
+
const schema = tool.inputSchema;
|
|
536
|
+
const required = schema.required ?? [];
|
|
537
|
+
const missing = required.filter((key) => args?.[key] === undefined || args?.[key] === null);
|
|
538
|
+
if (missing.length === 0)
|
|
539
|
+
return undefined;
|
|
540
|
+
return { missing, accepted: Object.keys(schema.properties ?? {}) };
|
|
541
|
+
}
|
|
526
542
|
// Handle tool calls
|
|
527
543
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
528
544
|
const { name, arguments: args } = request.params;
|
|
529
545
|
// Refresh cache for each request to pick up changes
|
|
530
546
|
invalidateCache();
|
|
547
|
+
// MCP-ARGS-1.2: reject calls missing required arguments before dispatch
|
|
548
|
+
const argCheck = findMissingRequiredArgs(name, args);
|
|
549
|
+
if (argCheck) {
|
|
550
|
+
return {
|
|
551
|
+
content: [
|
|
552
|
+
{
|
|
553
|
+
type: "text",
|
|
554
|
+
text: `Missing required argument(s) for ${name}: ${argCheck.missing.join(", ")}. Accepted arguments: ${argCheck.accepted.join(", ")}.`,
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
isError: true,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
531
560
|
// Create handler context for extracted handlers
|
|
532
561
|
const handlerContext = {
|
|
533
562
|
getRequirements,
|
package/dist/push/core.d.ts
CHANGED
|
@@ -72,6 +72,13 @@ export interface DryRunResult {
|
|
|
72
72
|
conflicts: ConflictInfo[];
|
|
73
73
|
totalRequirements: number;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Outcome of run-marker handling for one pushed file (IMPORT-2/3).
|
|
77
|
+
* Mirrors the server's ImportStatus.
|
|
78
|
+
*/
|
|
79
|
+
export type ImportStatus = "imported" | "invalid_marker" | "unsupported_version" | "already_used" | "ignored_update" | "none";
|
|
80
|
+
/** Base URL of the web app, where synced documents are reviewed. */
|
|
81
|
+
export declare const WEB_APP_URL = "https://app.dotrequirements.io";
|
|
75
82
|
/**
|
|
76
83
|
* Result of the execute phase.
|
|
77
84
|
*/
|
|
@@ -82,6 +89,17 @@ export interface PushResult {
|
|
|
82
89
|
fileName: string;
|
|
83
90
|
error: string;
|
|
84
91
|
}>;
|
|
92
|
+
/** IMPORT-3: marker failures that must be surfaced to the user, per file */
|
|
93
|
+
importWarnings: Array<{
|
|
94
|
+
fileName: string;
|
|
95
|
+
status: Extract<ImportStatus, "invalid_marker" | "unsupported_version" | "already_used">;
|
|
96
|
+
}>;
|
|
97
|
+
/** SYNC-LAND-1: each successfully synced document, with its web URL */
|
|
98
|
+
synced: Array<{
|
|
99
|
+
fileName: string;
|
|
100
|
+
documentId: string;
|
|
101
|
+
url: string;
|
|
102
|
+
}>;
|
|
85
103
|
}
|
|
86
104
|
/**
|
|
87
105
|
* Extract markdown content from a file, stripping YAML frontmatter.
|