@overshift/sfs 1.1.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 +215 -0
- package/dist/cli/coverage-output.d.ts +3 -0
- package/dist/cli/coverage-output.d.ts.map +1 -0
- package/dist/cli/coverage-output.js +74 -0
- package/dist/cli/coverage-output.js.map +1 -0
- package/dist/cli/coverage-types.d.ts +24 -0
- package/dist/cli/coverage-types.d.ts.map +1 -0
- package/dist/cli/coverage-types.js +2 -0
- package/dist/cli/coverage-types.js.map +1 -0
- package/dist/cli/coverage.d.ts +3 -0
- package/dist/cli/coverage.d.ts.map +1 -0
- package/dist/cli/coverage.js +69 -0
- package/dist/cli/coverage.js.map +1 -0
- package/dist/cli/init-templates.d.ts +5 -0
- package/dist/cli/init-templates.d.ts.map +1 -0
- package/dist/cli/init-templates.js +108 -0
- package/dist/cli/init-templates.js.map +1 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +170 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/run.d.ts +3 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +58 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/story-extractor.d.ts +7 -0
- package/dist/cli/story-extractor.d.ts.map +1 -0
- package/dist/cli/story-extractor.js +130 -0
- package/dist/cli/story-extractor.js.map +1 -0
- package/dist/cli/validate.d.ts +2 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +39 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/executor/assertion-executor.d.ts +7 -0
- package/dist/executor/assertion-executor.d.ts.map +1 -0
- package/dist/executor/assertion-executor.js +206 -0
- package/dist/executor/assertion-executor.js.map +1 -0
- package/dist/executor/config-loader.d.ts +5 -0
- package/dist/executor/config-loader.d.ts.map +1 -0
- package/dist/executor/config-loader.js +48 -0
- package/dist/executor/config-loader.js.map +1 -0
- package/dist/executor/executor-types.d.ts +64 -0
- package/dist/executor/executor-types.d.ts.map +1 -0
- package/dist/executor/executor-types.js +6 -0
- package/dist/executor/executor-types.js.map +1 -0
- package/dist/executor/retry-handler.d.ts +7 -0
- package/dist/executor/retry-handler.d.ts.map +1 -0
- package/dist/executor/retry-handler.js +32 -0
- package/dist/executor/retry-handler.js.map +1 -0
- package/dist/executor/run-context.d.ts +27 -0
- package/dist/executor/run-context.d.ts.map +1 -0
- package/dist/executor/run-context.js +52 -0
- package/dist/executor/run-context.js.map +1 -0
- package/dist/executor/run-engine.d.ts +9 -0
- package/dist/executor/run-engine.d.ts.map +1 -0
- package/dist/executor/run-engine.js +140 -0
- package/dist/executor/run-engine.js.map +1 -0
- package/dist/executor/session-manager.d.ts +16 -0
- package/dist/executor/session-manager.d.ts.map +1 -0
- package/dist/executor/session-manager.js +103 -0
- package/dist/executor/session-manager.js.map +1 -0
- package/dist/executor/shell-executor.d.ts +12 -0
- package/dist/executor/shell-executor.d.ts.map +1 -0
- package/dist/executor/shell-executor.js +66 -0
- package/dist/executor/shell-executor.js.map +1 -0
- package/dist/executor/step-executor.d.ts +6 -0
- package/dist/executor/step-executor.d.ts.map +1 -0
- package/dist/executor/step-executor.js +73 -0
- package/dist/executor/step-executor.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/assertion-parser.d.ts +3 -0
- package/dist/parser/assertion-parser.d.ts.map +1 -0
- package/dist/parser/assertion-parser.js +92 -0
- package/dist/parser/assertion-parser.js.map +1 -0
- package/dist/parser/command-parser.d.ts +3 -0
- package/dist/parser/command-parser.d.ts.map +1 -0
- package/dist/parser/command-parser.js +252 -0
- package/dist/parser/command-parser.js.map +1 -0
- package/dist/parser/config-parser.d.ts +16 -0
- package/dist/parser/config-parser.d.ts.map +1 -0
- package/dist/parser/config-parser.js +217 -0
- package/dist/parser/config-parser.js.map +1 -0
- package/dist/parser/sfs-parser.d.ts +3 -0
- package/dist/parser/sfs-parser.d.ts.map +1 -0
- package/dist/parser/sfs-parser.js +184 -0
- package/dist/parser/sfs-parser.js.map +1 -0
- package/dist/parser/step-parser.d.ts +3 -0
- package/dist/parser/step-parser.d.ts.map +1 -0
- package/dist/parser/step-parser.js +72 -0
- package/dist/parser/step-parser.js.map +1 -0
- package/dist/parser/types.d.ts +115 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js +3 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/utils/mcp-detector.d.ts +9 -0
- package/dist/utils/mcp-detector.d.ts.map +1 -0
- package/dist/utils/mcp-detector.js +62 -0
- package/dist/utils/mcp-detector.js.map +1 -0
- package/package.json +72 -0
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { writeFile, readFile, access, mkdir, readdir, copyFile, } from "fs/promises";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { detectMcpIntegration } from "../utils/mcp-detector.js";
|
|
5
|
+
import { CONFIG_TEMPLATE, EXAMPLE_SFS, GITIGNORE_ADDITIONS, SFS_SKILLS, } from "./init-templates.js";
|
|
6
|
+
export async function initCommand(options) {
|
|
7
|
+
console.log(chalk.blue("SFS - Initialize Project"));
|
|
8
|
+
console.log();
|
|
9
|
+
const filesToCreate = [
|
|
10
|
+
{ path: "sfs.config.md", content: CONFIG_TEMPLATE },
|
|
11
|
+
{ path: "stories/example.sfs", content: EXAMPLE_SFS },
|
|
12
|
+
];
|
|
13
|
+
for (const file of filesToCreate) {
|
|
14
|
+
const exists = await fileExists(file.path);
|
|
15
|
+
if (exists && !options.force) {
|
|
16
|
+
console.log(chalk.yellow(`⊘ ${file.path} already exists (use --force to overwrite)`));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
await ensureDir(dirname(file.path));
|
|
20
|
+
await writeFile(file.path, file.content);
|
|
21
|
+
console.log(chalk.green(`✓ Created ${file.path}`));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Update .gitignore
|
|
25
|
+
try {
|
|
26
|
+
const gitignorePath = ".gitignore";
|
|
27
|
+
let gitignoreContent = "";
|
|
28
|
+
try {
|
|
29
|
+
gitignoreContent = await readFile(gitignorePath, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// File doesn't exist
|
|
33
|
+
}
|
|
34
|
+
if (!gitignoreContent.includes("sfs.config.local.md")) {
|
|
35
|
+
await writeFile(gitignorePath, gitignoreContent + GITIGNORE_ADDITIONS);
|
|
36
|
+
console.log(chalk.green("✓ Updated .gitignore"));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log(chalk.gray("⊘ .gitignore already contains SFS entries"));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
console.log(chalk.yellow("⚠ Could not update .gitignore"));
|
|
44
|
+
}
|
|
45
|
+
// Copy skills to destination
|
|
46
|
+
const skillsDir = await detectOrCreateSkillsDir(options.skillsDir);
|
|
47
|
+
if (skillsDir) {
|
|
48
|
+
await copySkills(skillsDir, options.force ?? false);
|
|
49
|
+
}
|
|
50
|
+
// Check for MCP integration
|
|
51
|
+
await checkMcpIntegration();
|
|
52
|
+
console.log();
|
|
53
|
+
console.log(chalk.green("SFS initialized successfully!"));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log("Next steps:");
|
|
56
|
+
console.log(chalk.gray(" 1. Edit sfs.config.md with your application settings"));
|
|
57
|
+
console.log(chalk.gray(" 2. Define personas with credentials for your environment"));
|
|
58
|
+
console.log(chalk.gray(" 3. Create your first flow: stories/US-XXX-001.sfs"));
|
|
59
|
+
console.log(chalk.gray(" 4. Run: npx sfs run"));
|
|
60
|
+
}
|
|
61
|
+
async function fileExists(path) {
|
|
62
|
+
try {
|
|
63
|
+
await access(path);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function ensureDir(dir) {
|
|
71
|
+
if (dir && dir !== ".") {
|
|
72
|
+
await mkdir(dir, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function detectOrCreateSkillsDir(preferred) {
|
|
76
|
+
// Check preferred location first
|
|
77
|
+
if (preferred) {
|
|
78
|
+
await ensureDir(preferred);
|
|
79
|
+
return preferred;
|
|
80
|
+
}
|
|
81
|
+
// Check for existing skills directories
|
|
82
|
+
const candidates = [".claude/skills", ".agent/skills"];
|
|
83
|
+
for (const candidate of candidates) {
|
|
84
|
+
if (await fileExists(candidate)) {
|
|
85
|
+
return candidate;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Default to .claude/skills
|
|
89
|
+
const defaultDir = ".claude/skills";
|
|
90
|
+
await ensureDir(defaultDir);
|
|
91
|
+
return defaultDir;
|
|
92
|
+
}
|
|
93
|
+
async function copySkills(destDir, force) {
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(chalk.blue(`Copying SFS skills to ${destDir}...`));
|
|
96
|
+
// Get the SFS package skills directory
|
|
97
|
+
// In development: relative to this file
|
|
98
|
+
// In production: from the package's skills directory
|
|
99
|
+
const packageSkillsDir = await findPackageSkillsDir();
|
|
100
|
+
if (!packageSkillsDir) {
|
|
101
|
+
console.log(chalk.yellow("⚠ Could not locate SFS skills directory"));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const skillFile of SFS_SKILLS) {
|
|
105
|
+
const srcPath = join(packageSkillsDir, skillFile);
|
|
106
|
+
const destPath = join(destDir, skillFile);
|
|
107
|
+
try {
|
|
108
|
+
const srcExists = await fileExists(srcPath);
|
|
109
|
+
if (!srcExists) {
|
|
110
|
+
console.log(chalk.yellow(`⚠ Skill not found: ${skillFile}`));
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const destExists = await fileExists(destPath);
|
|
114
|
+
if (destExists && !force) {
|
|
115
|
+
console.log(chalk.gray(`⊘ ${skillFile} already exists`));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await copyFile(srcPath, destPath);
|
|
119
|
+
console.log(chalk.green(`✓ Copied ${skillFile}`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.log(chalk.yellow(`⚠ Could not copy ${skillFile}: ${err instanceof Error ? err.message : "Unknown error"}`));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function findPackageSkillsDir() {
|
|
128
|
+
// Try to find the skills directory relative to the current module
|
|
129
|
+
const candidates = [
|
|
130
|
+
// Development: when running from source
|
|
131
|
+
join(process.cwd(), ".claude", "skills"),
|
|
132
|
+
// Production: when installed as package (look in node_modules)
|
|
133
|
+
join(process.cwd(), "node_modules", "@sfs", "cli", "skills"),
|
|
134
|
+
join(process.cwd(), "node_modules", "sfs", "skills"),
|
|
135
|
+
];
|
|
136
|
+
for (const candidate of candidates) {
|
|
137
|
+
if (await fileExists(candidate)) {
|
|
138
|
+
try {
|
|
139
|
+
const files = await readdir(candidate);
|
|
140
|
+
// Check if it has at least one of our skill files
|
|
141
|
+
if (files.some((f) => SFS_SKILLS.includes(f))) {
|
|
142
|
+
return candidate;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Continue to next candidate
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
async function checkMcpIntegration() {
|
|
153
|
+
const detection = await detectMcpIntegration();
|
|
154
|
+
console.log();
|
|
155
|
+
if (detection.hasPlaywright) {
|
|
156
|
+
console.log(chalk.green("✓ Playwright MCP server detected"));
|
|
157
|
+
}
|
|
158
|
+
else if (detection.detected) {
|
|
159
|
+
console.log(chalk.yellow("⚠ MCP servers found but no Playwright MCP detected"));
|
|
160
|
+
console.log(chalk.gray(" SFS works best with the Playwright MCP server"));
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(chalk.gray("ℹ No MCP servers detected"));
|
|
164
|
+
console.log(chalk.gray(" Configure Playwright MCP in .claude/ for browser automation"));
|
|
165
|
+
}
|
|
166
|
+
if (detection.guidance) {
|
|
167
|
+
console.log(chalk.gray(` ${detection.guidance}`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA0Df"}
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { glob } from "glob";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { createRunEngine } from "../executor/run-engine.js";
|
|
4
|
+
export async function runCommand(files, options) {
|
|
5
|
+
console.log(chalk.blue("SFS - StoryFlowSteps Runner"));
|
|
6
|
+
console.log();
|
|
7
|
+
// Find SFS files
|
|
8
|
+
const sfsFiles = files.length > 0
|
|
9
|
+
? await resolveFiles(files)
|
|
10
|
+
: await glob("**/*.sfs", { ignore: ["node_modules/**"] });
|
|
11
|
+
if (sfsFiles.length === 0) {
|
|
12
|
+
console.log(chalk.yellow("No .sfs files found"));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log(chalk.gray(`Found ${sfsFiles.length} SFS file(s)`));
|
|
16
|
+
if (options.env) {
|
|
17
|
+
console.log(chalk.gray(`Environment: ${options.env}`));
|
|
18
|
+
}
|
|
19
|
+
console.log();
|
|
20
|
+
const engine = createRunEngine();
|
|
21
|
+
const summary = await engine.run(sfsFiles, options);
|
|
22
|
+
// Display results
|
|
23
|
+
for (const result of summary.results) {
|
|
24
|
+
if (result.skipped) {
|
|
25
|
+
console.log(chalk.yellow(`⊘ SKIP ${result.file}${result.skipReason ? ` (${result.skipReason})` : ""}`));
|
|
26
|
+
}
|
|
27
|
+
else if (result.success) {
|
|
28
|
+
console.log(chalk.green(`✓ PASS ${result.file} (${result.totalDuration}ms)`));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log(chalk.red(`✗ FAIL ${result.file} (${result.totalDuration}ms)`));
|
|
32
|
+
if (result.error) {
|
|
33
|
+
console.log(chalk.red(` ${result.error.message}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(chalk.gray("─".repeat(40)));
|
|
39
|
+
console.log(`${chalk.green(`${summary.passed} passed`)}, ${chalk.red(`${summary.failed} failed`)}, ${chalk.yellow(`${summary.skipped} skipped`)}`);
|
|
40
|
+
console.log(chalk.gray(`Total: ${summary.totalDuration}ms`));
|
|
41
|
+
if (summary.failed > 0) {
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function resolveFiles(patterns) {
|
|
46
|
+
const files = [];
|
|
47
|
+
for (const pattern of patterns) {
|
|
48
|
+
if (pattern.includes("*")) {
|
|
49
|
+
const matches = await glob(pattern, { ignore: ["node_modules/**"] });
|
|
50
|
+
files.push(...matches);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
files.push(pattern);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return [...new Set(files)];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UserStory } from "./coverage-types.js";
|
|
2
|
+
export declare function extractStoryIdFromFilename(filePath: string): string | undefined;
|
|
3
|
+
export declare function extractStoryTitle(filePath: string): Promise<string | undefined>;
|
|
4
|
+
export declare function extractStoriesFromMultiFile(filePath: string): Promise<UserStory[]>;
|
|
5
|
+
export declare function deduplicateStories(stories: UserStory[]): UserStory[];
|
|
6
|
+
export declare function extractSfsStoryId(filePath: string): Promise<string | undefined>;
|
|
7
|
+
//# sourceMappingURL=story-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-extractor.d.ts","sourceRoot":"","sources":["../../src/cli/story-extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,EAAE,CAAC,CAsFtB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAcpE;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { basename } from "path";
|
|
3
|
+
export function extractStoryIdFromFilename(filePath) {
|
|
4
|
+
const name = basename(filePath, ".md");
|
|
5
|
+
const match = name.match(/^(US-[A-Z0-9-]+)/i);
|
|
6
|
+
if (match?.[1]) {
|
|
7
|
+
return match[1].toUpperCase();
|
|
8
|
+
}
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
export async function extractStoryTitle(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
const content = await readFile(filePath, "utf-8");
|
|
14
|
+
const lines = content.split("\n");
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const match = line.match(/^#\s+(.+)$/);
|
|
17
|
+
if (match?.[1]) {
|
|
18
|
+
return match[1].trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Ignore read errors
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export async function extractStoriesFromMultiFile(filePath) {
|
|
28
|
+
const stories = [];
|
|
29
|
+
try {
|
|
30
|
+
const content = await readFile(filePath, "utf-8");
|
|
31
|
+
const lines = content.split("\n");
|
|
32
|
+
let currentStoryId;
|
|
33
|
+
let currentTitle;
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
// Look for story IDs in headings: ## US-XXX-001: Title or ### US-XXX-001 - Title
|
|
36
|
+
const headingMatch = line.match(/^#{1,4}\s+(US-[A-Z0-9-]+)[\s:|-]+(.*)$/i);
|
|
37
|
+
if (headingMatch?.[1]) {
|
|
38
|
+
if (currentStoryId) {
|
|
39
|
+
stories.push({
|
|
40
|
+
id: currentStoryId,
|
|
41
|
+
title: currentTitle,
|
|
42
|
+
path: filePath,
|
|
43
|
+
source: "multi",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
currentStoryId = headingMatch[1].toUpperCase();
|
|
47
|
+
currentTitle = headingMatch[2]?.trim() || undefined;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
// Also look for story IDs in bold: **US-XXX-001** or **US-XXX-001:** or **US-XXX-001**:
|
|
51
|
+
const boldMatch = line.match(/\*\*(US-[A-Z0-9-]+):?\*\*[\s:|-]*(.*)$/i);
|
|
52
|
+
if (boldMatch?.[1]) {
|
|
53
|
+
if (currentStoryId) {
|
|
54
|
+
stories.push({
|
|
55
|
+
id: currentStoryId,
|
|
56
|
+
title: currentTitle,
|
|
57
|
+
path: filePath,
|
|
58
|
+
source: "multi",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
currentStoryId = boldMatch[1].toUpperCase();
|
|
62
|
+
currentTitle = boldMatch[2]?.trim() || undefined;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// Look for story IDs in list items: - US-XXX-001: Title or * US-XXX-001 - Title
|
|
66
|
+
const listMatch = line.match(/^[\s]*[-*]\s+(US-[A-Z0-9-]+)[\s:|-]+(.*)$/i);
|
|
67
|
+
if (listMatch?.[1]) {
|
|
68
|
+
stories.push({
|
|
69
|
+
id: listMatch[1].toUpperCase(),
|
|
70
|
+
title: listMatch[2]?.trim() || undefined,
|
|
71
|
+
path: filePath,
|
|
72
|
+
source: "multi",
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Look for story IDs in table rows: | US-XXX-001 | Title | ... |
|
|
77
|
+
const tableMatch = line.match(/\|\s*(US-[A-Z0-9-]+)\s*\|([^|]*)\|/i);
|
|
78
|
+
if (tableMatch?.[1]) {
|
|
79
|
+
stories.push({
|
|
80
|
+
id: tableMatch[1].toUpperCase(),
|
|
81
|
+
title: tableMatch[2]?.trim() || undefined,
|
|
82
|
+
path: filePath,
|
|
83
|
+
source: "multi",
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Don't forget the last story
|
|
89
|
+
if (currentStoryId) {
|
|
90
|
+
stories.push({
|
|
91
|
+
id: currentStoryId,
|
|
92
|
+
title: currentTitle,
|
|
93
|
+
path: filePath,
|
|
94
|
+
source: "multi",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Ignore read errors
|
|
100
|
+
}
|
|
101
|
+
return stories;
|
|
102
|
+
}
|
|
103
|
+
export function deduplicateStories(stories) {
|
|
104
|
+
const storyMap = new Map();
|
|
105
|
+
for (const story of stories) {
|
|
106
|
+
const existing = storyMap.get(story.id);
|
|
107
|
+
if (!existing ||
|
|
108
|
+
(existing.source === "multi" && story.source === "single")) {
|
|
109
|
+
storyMap.set(story.id, story);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [...storyMap.values()];
|
|
113
|
+
}
|
|
114
|
+
export async function extractSfsStoryId(filePath) {
|
|
115
|
+
try {
|
|
116
|
+
const content = await readFile(filePath, "utf-8");
|
|
117
|
+
const lines = content.split("\n");
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
const match = line.match(/^STORY\s+(US-[A-Z0-9-]+)/i);
|
|
120
|
+
if (match?.[1]) {
|
|
121
|
+
return match[1].toUpperCase();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Ignore read errors
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=story-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-extractor.js","sourceRoot":"","sources":["../../src/cli/story-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAIA,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CpE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { parseSfsFile } from "../parser/sfs-parser.js";
|
|
4
|
+
export async function validateCommand(files) {
|
|
5
|
+
console.log(chalk.blue("SFS - Syntax Validator"));
|
|
6
|
+
console.log();
|
|
7
|
+
let hasErrors = false;
|
|
8
|
+
for (const file of files) {
|
|
9
|
+
try {
|
|
10
|
+
const content = await readFile(file, "utf-8");
|
|
11
|
+
const result = parseSfsFile(content, file);
|
|
12
|
+
if (result.errors.length > 0) {
|
|
13
|
+
console.log(chalk.red(`✗ ${file}`));
|
|
14
|
+
for (const error of result.errors) {
|
|
15
|
+
console.log(chalk.red(` Line ${error.line}: ${error.message}`));
|
|
16
|
+
}
|
|
17
|
+
hasErrors = true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.log(chalk.green(`✓ ${file}`));
|
|
21
|
+
if (result.warnings.length > 0) {
|
|
22
|
+
for (const warning of result.warnings) {
|
|
23
|
+
console.log(chalk.yellow(` Line ${warning.line}: ${warning.message}`));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.log(chalk.red(`✗ ${file}`));
|
|
30
|
+
console.log(chalk.red(` Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
31
|
+
hasErrors = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
console.log();
|
|
35
|
+
if (hasErrors) {
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAe;IACnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAE3C,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;gBACpC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACnE,CAAC;gBACD,SAAS,GAAG,IAAI,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC/B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBACtC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CAAC,UAAU,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAC3D,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CACP,YAAY,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACvE,CACF,CAAC;YACF,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SfsAssertion } from "../parser/types.js";
|
|
2
|
+
import type { AssertionResult, RetryConfig } from "./executor-types.js";
|
|
3
|
+
export interface AssertionExecutor {
|
|
4
|
+
execute(assertion: SfsAssertion, variables?: Record<string, string>, retryConfig?: RetryConfig): Promise<AssertionResult>;
|
|
5
|
+
}
|
|
6
|
+
export declare function createAssertionExecutor(): AssertionExecutor;
|
|
7
|
+
//# sourceMappingURL=assertion-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertion-executor.d.ts","sourceRoot":"","sources":["../../src/executor/assertion-executor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAQxE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CACL,SAAS,EAAE,YAAY,EACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,eAAe,CAAC,CAAC;CAC7B;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAiC3D"}
|