specky-sdd 1.0.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/LICENSE +22 -0
- package/README.md +446 -0
- package/dist/constants.d.ts +68 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +120 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +8 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +18 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/pipeline.d.ts +296 -0
- package/dist/schemas/pipeline.d.ts.map +1 -0
- package/dist/schemas/pipeline.js +132 -0
- package/dist/schemas/pipeline.js.map +1 -0
- package/dist/schemas/transcript.d.ts +59 -0
- package/dist/schemas/transcript.d.ts.map +1 -0
- package/dist/schemas/transcript.js +61 -0
- package/dist/schemas/transcript.js.map +1 -0
- package/dist/schemas/utility.d.ts +92 -0
- package/dist/schemas/utility.d.ts.map +1 -0
- package/dist/schemas/utility.js +82 -0
- package/dist/schemas/utility.js.map +1 -0
- package/dist/services/codebase-scanner.d.ts +24 -0
- package/dist/services/codebase-scanner.d.ts.map +1 -0
- package/dist/services/codebase-scanner.js +185 -0
- package/dist/services/codebase-scanner.js.map +1 -0
- package/dist/services/ears-validator.d.ts +29 -0
- package/dist/services/ears-validator.d.ts.map +1 -0
- package/dist/services/ears-validator.js +163 -0
- package/dist/services/ears-validator.js.map +1 -0
- package/dist/services/file-manager.d.ts +56 -0
- package/dist/services/file-manager.d.ts.map +1 -0
- package/dist/services/file-manager.js +203 -0
- package/dist/services/file-manager.js.map +1 -0
- package/dist/services/state-machine.d.ts +46 -0
- package/dist/services/state-machine.d.ts.map +1 -0
- package/dist/services/state-machine.js +167 -0
- package/dist/services/state-machine.js.map +1 -0
- package/dist/services/template-engine.d.ts +37 -0
- package/dist/services/template-engine.d.ts.map +1 -0
- package/dist/services/template-engine.js +111 -0
- package/dist/services/template-engine.js.map +1 -0
- package/dist/services/transcript-parser.d.ts +61 -0
- package/dist/services/transcript-parser.d.ts.map +1 -0
- package/dist/services/transcript-parser.js +810 -0
- package/dist/services/transcript-parser.js.map +1 -0
- package/dist/tools/analysis.d.ts +10 -0
- package/dist/tools/analysis.d.ts.map +1 -0
- package/dist/tools/analysis.js +95 -0
- package/dist/tools/analysis.js.map +1 -0
- package/dist/tools/pipeline.d.ts +11 -0
- package/dist/tools/pipeline.d.ts.map +1 -0
- package/dist/tools/pipeline.js +583 -0
- package/dist/tools/pipeline.js.map +1 -0
- package/dist/tools/transcript.d.ts +14 -0
- package/dist/tools/transcript.d.ts.map +1 -0
- package/dist/tools/transcript.js +813 -0
- package/dist/tools/transcript.js.map +1 -0
- package/dist/tools/utility.d.ts +10 -0
- package/dist/tools/utility.d.ts.map +1 -0
- package/dist/tools/utility.js +239 -0
- package/dist/tools/utility.js.map +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
- package/templates/analysis.md +54 -0
- package/templates/bugfix.md +45 -0
- package/templates/constitution.md +56 -0
- package/templates/design.md +47 -0
- package/templates/specification.md +49 -0
- package/templates/sync-report.md +43 -0
- package/templates/tasks.md +38 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for transcript automation tools.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { specDirSchema } from "./common.js";
|
|
6
|
+
export const importTranscriptInputSchema = z.object({
|
|
7
|
+
file_path: z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.describe("Path to transcript file (.vtt, .srt, .txt, .md) relative to workspace root"),
|
|
11
|
+
raw_text: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Raw transcript text (alternative to file_path — paste directly)"),
|
|
15
|
+
format: z
|
|
16
|
+
.enum(["vtt", "srt", "txt", "md", "auto"])
|
|
17
|
+
.default("auto")
|
|
18
|
+
.describe("Transcript format. Use 'auto' to detect from file extension or content."),
|
|
19
|
+
spec_dir: specDirSchema,
|
|
20
|
+
}).strict();
|
|
21
|
+
export const autoPipelineInputSchema = z.object({
|
|
22
|
+
file_path: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Path to transcript file (.vtt, .srt, .txt, .md) relative to workspace root"),
|
|
26
|
+
raw_text: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Raw transcript text (alternative to file_path — paste directly)"),
|
|
30
|
+
project_name: z
|
|
31
|
+
.string()
|
|
32
|
+
.min(1)
|
|
33
|
+
.max(100)
|
|
34
|
+
.regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/, "Must be kebab-case")
|
|
35
|
+
.describe("Project name in kebab-case"),
|
|
36
|
+
format: z
|
|
37
|
+
.enum(["vtt", "srt", "txt", "md", "auto"])
|
|
38
|
+
.default("auto")
|
|
39
|
+
.describe("Transcript format"),
|
|
40
|
+
spec_dir: specDirSchema,
|
|
41
|
+
principles: z
|
|
42
|
+
.array(z.string())
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Override project principles (auto-extracted from transcript if omitted)"),
|
|
45
|
+
force: z
|
|
46
|
+
.boolean()
|
|
47
|
+
.default(false)
|
|
48
|
+
.describe("Overwrite existing spec files"),
|
|
49
|
+
}).strict();
|
|
50
|
+
export const batchTranscriptsInputSchema = z.object({
|
|
51
|
+
transcripts_dir: z
|
|
52
|
+
.string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.describe("Path to folder containing transcript files (.vtt, .srt, .txt, .md) relative to workspace root. Example: 'transcripts' or 'OneDrive/Meeting Transcripts'"),
|
|
55
|
+
spec_dir: specDirSchema,
|
|
56
|
+
force: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.default(false)
|
|
59
|
+
.describe("Overwrite existing spec files if true"),
|
|
60
|
+
}).strict();
|
|
61
|
+
//# sourceMappingURL=transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/schemas/transcript.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,4EAA4E,CAAC;IACzF,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,iEAAiE,CAAC;IAC9E,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;SACzC,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,yEAAyE,CAAC;IACtF,QAAQ,EAAE,aAAa;CACxB,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,4EAA4E,CAAC;IACzF,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,iEAAiE,CAAC;IAC9E,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,KAAK,CAAC,yCAAyC,EAAE,oBAAoB,CAAC;SACtE,QAAQ,CAAC,4BAA4B,CAAC;IACzC,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;SACzC,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,mBAAmB,CAAC;IAChC,QAAQ,EAAE,aAAa;IACvB,UAAU,EAAE,CAAC;SACV,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CAAC,yEAAyE,CAAC;IACtF,KAAK,EAAE,CAAC;SACL,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,+BAA+B,CAAC;CAC7C,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,yJAAyJ,CAAC;IACtK,QAAQ,EAAE,aAAa;IACvB,KAAK,EAAE,CAAC;SACL,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,uCAAuC,CAAC;CACrD,CAAC,CAAC,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for all 6 utility tool inputs.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const getStatusInputSchema: z.ZodObject<{
|
|
6
|
+
spec_dir: z.ZodDefault<z.ZodString>;
|
|
7
|
+
feature_number: z.ZodDefault<z.ZodString>;
|
|
8
|
+
}, "strict", z.ZodTypeAny, {
|
|
9
|
+
spec_dir: string;
|
|
10
|
+
feature_number: string;
|
|
11
|
+
}, {
|
|
12
|
+
spec_dir?: string | undefined;
|
|
13
|
+
feature_number?: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const getTemplateInputSchema: z.ZodObject<{
|
|
16
|
+
template_name: z.ZodEnum<["constitution", "specification", "design", "tasks", "analysis", "bugfix", "sync_report"]>;
|
|
17
|
+
}, "strict", z.ZodTypeAny, {
|
|
18
|
+
template_name: "design" | "tasks" | "constitution" | "specification" | "analysis" | "bugfix" | "sync_report";
|
|
19
|
+
}, {
|
|
20
|
+
template_name: "design" | "tasks" | "constitution" | "specification" | "analysis" | "bugfix" | "sync_report";
|
|
21
|
+
}>;
|
|
22
|
+
export declare const writeBugfixInputSchema: z.ZodObject<{
|
|
23
|
+
bug_title: z.ZodString;
|
|
24
|
+
current_behavior: z.ZodString;
|
|
25
|
+
expected_behavior: z.ZodString;
|
|
26
|
+
unchanged_behavior: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
27
|
+
root_cause: z.ZodOptional<z.ZodString>;
|
|
28
|
+
test_plan: z.ZodOptional<z.ZodString>;
|
|
29
|
+
spec_dir: z.ZodDefault<z.ZodString>;
|
|
30
|
+
feature_number: z.ZodDefault<z.ZodString>;
|
|
31
|
+
}, "strict", z.ZodTypeAny, {
|
|
32
|
+
spec_dir: string;
|
|
33
|
+
feature_number: string;
|
|
34
|
+
bug_title: string;
|
|
35
|
+
current_behavior: string;
|
|
36
|
+
expected_behavior: string;
|
|
37
|
+
unchanged_behavior?: string[] | undefined;
|
|
38
|
+
root_cause?: string | undefined;
|
|
39
|
+
test_plan?: string | undefined;
|
|
40
|
+
}, {
|
|
41
|
+
bug_title: string;
|
|
42
|
+
current_behavior: string;
|
|
43
|
+
expected_behavior: string;
|
|
44
|
+
spec_dir?: string | undefined;
|
|
45
|
+
feature_number?: string | undefined;
|
|
46
|
+
unchanged_behavior?: string[] | undefined;
|
|
47
|
+
root_cause?: string | undefined;
|
|
48
|
+
test_plan?: string | undefined;
|
|
49
|
+
}>;
|
|
50
|
+
export declare const checkSyncInputSchema: z.ZodObject<{
|
|
51
|
+
spec_dir: z.ZodDefault<z.ZodString>;
|
|
52
|
+
feature_number: z.ZodDefault<z.ZodString>;
|
|
53
|
+
code_paths: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
54
|
+
}, "strict", z.ZodTypeAny, {
|
|
55
|
+
spec_dir: string;
|
|
56
|
+
feature_number: string;
|
|
57
|
+
code_paths?: string[] | undefined;
|
|
58
|
+
}, {
|
|
59
|
+
spec_dir?: string | undefined;
|
|
60
|
+
feature_number?: string | undefined;
|
|
61
|
+
code_paths?: string[] | undefined;
|
|
62
|
+
}>;
|
|
63
|
+
export declare const scanCodebaseInputSchema: z.ZodObject<{
|
|
64
|
+
depth: z.ZodDefault<z.ZodNumber>;
|
|
65
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
66
|
+
}, "strict", z.ZodTypeAny, {
|
|
67
|
+
depth: number;
|
|
68
|
+
exclude?: string[] | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
depth?: number | undefined;
|
|
71
|
+
exclude?: string[] | undefined;
|
|
72
|
+
}>;
|
|
73
|
+
export declare const amendInputSchema: z.ZodObject<{
|
|
74
|
+
rationale: z.ZodString;
|
|
75
|
+
articles_affected: z.ZodArray<z.ZodString, "many">;
|
|
76
|
+
changes_description: z.ZodString;
|
|
77
|
+
spec_dir: z.ZodDefault<z.ZodString>;
|
|
78
|
+
feature_number: z.ZodDefault<z.ZodString>;
|
|
79
|
+
}, "strict", z.ZodTypeAny, {
|
|
80
|
+
spec_dir: string;
|
|
81
|
+
feature_number: string;
|
|
82
|
+
rationale: string;
|
|
83
|
+
articles_affected: string[];
|
|
84
|
+
changes_description: string;
|
|
85
|
+
}, {
|
|
86
|
+
rationale: string;
|
|
87
|
+
articles_affected: string[];
|
|
88
|
+
changes_description: string;
|
|
89
|
+
spec_dir?: string | undefined;
|
|
90
|
+
feature_number?: string | undefined;
|
|
91
|
+
}>;
|
|
92
|
+
//# sourceMappingURL=utility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utility.d.ts","sourceRoot":"","sources":["../../src/schemas/utility.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,oBAAoB;;;;;;;;;EAGtB,CAAC;AAEZ,eAAO,MAAM,sBAAsB;;;;;;EAIxB,CAAC;AAEZ,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BxB,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAOtB,CAAC;AAEZ,eAAO,MAAM,uBAAuB;;;;;;;;;EAYzB,CAAC;AAEZ,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAelB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for all 6 utility tool inputs.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { specDirSchema, featureNumberSchema } from "./common.js";
|
|
6
|
+
import { TEMPLATE_NAMES } from "../constants.js";
|
|
7
|
+
export const getStatusInputSchema = z.object({
|
|
8
|
+
spec_dir: specDirSchema,
|
|
9
|
+
feature_number: featureNumberSchema,
|
|
10
|
+
}).strict();
|
|
11
|
+
export const getTemplateInputSchema = z.object({
|
|
12
|
+
template_name: z
|
|
13
|
+
.enum(TEMPLATE_NAMES)
|
|
14
|
+
.describe("Name of the template to retrieve"),
|
|
15
|
+
}).strict();
|
|
16
|
+
export const writeBugfixInputSchema = z.object({
|
|
17
|
+
bug_title: z
|
|
18
|
+
.string()
|
|
19
|
+
.min(1)
|
|
20
|
+
.max(200)
|
|
21
|
+
.describe("Short title describing the bug"),
|
|
22
|
+
current_behavior: z
|
|
23
|
+
.string()
|
|
24
|
+
.min(1)
|
|
25
|
+
.describe("What currently happens (the bug)"),
|
|
26
|
+
expected_behavior: z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.describe("What should happen instead"),
|
|
30
|
+
unchanged_behavior: z
|
|
31
|
+
.array(z.string())
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Behaviors that must remain unchanged after the fix"),
|
|
34
|
+
root_cause: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Root cause analysis (if known)"),
|
|
38
|
+
test_plan: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("How to verify the fix"),
|
|
42
|
+
spec_dir: specDirSchema,
|
|
43
|
+
feature_number: featureNumberSchema,
|
|
44
|
+
}).strict();
|
|
45
|
+
export const checkSyncInputSchema = z.object({
|
|
46
|
+
spec_dir: specDirSchema,
|
|
47
|
+
feature_number: featureNumberSchema,
|
|
48
|
+
code_paths: z
|
|
49
|
+
.array(z.string())
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Paths to implementation files to check against spec"),
|
|
52
|
+
}).strict();
|
|
53
|
+
export const scanCodebaseInputSchema = z.object({
|
|
54
|
+
depth: z
|
|
55
|
+
.number()
|
|
56
|
+
.int()
|
|
57
|
+
.min(1)
|
|
58
|
+
.max(5)
|
|
59
|
+
.default(3)
|
|
60
|
+
.describe("Scan depth (1-5, default 3)"),
|
|
61
|
+
exclude: z
|
|
62
|
+
.array(z.string())
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Glob patterns to exclude (defaults to node_modules, .git, dist)"),
|
|
65
|
+
}).strict();
|
|
66
|
+
export const amendInputSchema = z.object({
|
|
67
|
+
rationale: z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1)
|
|
70
|
+
.describe("Why this amendment is needed"),
|
|
71
|
+
articles_affected: z
|
|
72
|
+
.array(z.string())
|
|
73
|
+
.min(1)
|
|
74
|
+
.describe("Which Constitution articles are affected"),
|
|
75
|
+
changes_description: z
|
|
76
|
+
.string()
|
|
77
|
+
.min(1)
|
|
78
|
+
.describe("Description of the changes"),
|
|
79
|
+
spec_dir: specDirSchema,
|
|
80
|
+
feature_number: featureNumberSchema,
|
|
81
|
+
}).strict();
|
|
82
|
+
//# sourceMappingURL=utility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utility.js","sourceRoot":"","sources":["../../src/schemas/utility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodebaseScanner — Project structure and tech stack detection.
|
|
3
|
+
*/
|
|
4
|
+
import type { CodebaseSummary, TechStack } from "../types.js";
|
|
5
|
+
import type { FileManager } from "./file-manager.js";
|
|
6
|
+
export declare class CodebaseScanner {
|
|
7
|
+
private fileManager;
|
|
8
|
+
constructor(fileManager: FileManager);
|
|
9
|
+
/**
|
|
10
|
+
* Full codebase scan: directory tree + tech stack detection.
|
|
11
|
+
*/
|
|
12
|
+
scan(depth?: number, exclude?: readonly string[]): Promise<CodebaseSummary>;
|
|
13
|
+
/**
|
|
14
|
+
* Detect tech stack by reading package manifests.
|
|
15
|
+
*/
|
|
16
|
+
detectTechStack(): Promise<TechStack>;
|
|
17
|
+
private detectFromManifest;
|
|
18
|
+
private detectFromPackageJson;
|
|
19
|
+
private detectFromRequirementsTxt;
|
|
20
|
+
private detectFromPyprojectToml;
|
|
21
|
+
private detectFromGoMod;
|
|
22
|
+
private countNodes;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=codebase-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codebase-scanner.d.ts","sourceRoot":"","sources":["../../src/services/codebase-scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,qBAAa,eAAe;IACd,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,WAAW;IAE5C;;OAEG;IACG,IAAI,CACR,KAAK,GAAE,MAA2B,EAClC,OAAO,GAAE,SAAS,MAAM,EAA6B,GACpD,OAAO,CAAC,eAAe,CAAC;IAa3B;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC;IAqB3C,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,yBAAyB;IAejC,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,UAAU;CAmBnB"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodebaseScanner — Project structure and tech stack detection.
|
|
3
|
+
*/
|
|
4
|
+
import { PACKAGE_MANIFESTS, DEFAULT_SCAN_DEPTH, DEFAULT_EXCLUDE_PATTERNS } from "../constants.js";
|
|
5
|
+
export class CodebaseScanner {
|
|
6
|
+
fileManager;
|
|
7
|
+
constructor(fileManager) {
|
|
8
|
+
this.fileManager = fileManager;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Full codebase scan: directory tree + tech stack detection.
|
|
12
|
+
*/
|
|
13
|
+
async scan(depth = DEFAULT_SCAN_DEPTH, exclude = DEFAULT_EXCLUDE_PATTERNS) {
|
|
14
|
+
const tree = await this.fileManager.scanDirectory(".", depth, exclude);
|
|
15
|
+
const techStack = await this.detectTechStack();
|
|
16
|
+
const counts = this.countNodes(tree);
|
|
17
|
+
return {
|
|
18
|
+
tree,
|
|
19
|
+
tech_stack: techStack,
|
|
20
|
+
total_files: counts.files,
|
|
21
|
+
total_dirs: counts.dirs,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Detect tech stack by reading package manifests.
|
|
26
|
+
*/
|
|
27
|
+
async detectTechStack() {
|
|
28
|
+
for (const manifest of PACKAGE_MANIFESTS) {
|
|
29
|
+
try {
|
|
30
|
+
const content = await this.fileManager.readProjectFile(manifest);
|
|
31
|
+
const result = this.detectFromManifest(manifest, content);
|
|
32
|
+
if (result) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Manifest doesn't exist, try next
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
language: "unknown",
|
|
43
|
+
package_manager: "unknown",
|
|
44
|
+
runtime: "unknown",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
detectFromManifest(manifestName, content) {
|
|
48
|
+
switch (manifestName) {
|
|
49
|
+
case "package.json":
|
|
50
|
+
return this.detectFromPackageJson(content);
|
|
51
|
+
case "requirements.txt":
|
|
52
|
+
return this.detectFromRequirementsTxt(content);
|
|
53
|
+
case "pyproject.toml":
|
|
54
|
+
return this.detectFromPyprojectToml(content);
|
|
55
|
+
case "go.mod":
|
|
56
|
+
return this.detectFromGoMod(content);
|
|
57
|
+
case "Cargo.toml":
|
|
58
|
+
return {
|
|
59
|
+
language: "Rust",
|
|
60
|
+
package_manager: "cargo",
|
|
61
|
+
runtime: "Rust",
|
|
62
|
+
};
|
|
63
|
+
case "pom.xml":
|
|
64
|
+
return {
|
|
65
|
+
language: "Java",
|
|
66
|
+
package_manager: "maven",
|
|
67
|
+
runtime: "JVM",
|
|
68
|
+
};
|
|
69
|
+
case "build.gradle":
|
|
70
|
+
return {
|
|
71
|
+
language: "Java",
|
|
72
|
+
package_manager: "gradle",
|
|
73
|
+
runtime: "JVM",
|
|
74
|
+
};
|
|
75
|
+
default:
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
detectFromPackageJson(content) {
|
|
80
|
+
try {
|
|
81
|
+
const pkg = JSON.parse(content);
|
|
82
|
+
const deps = {
|
|
83
|
+
...pkg["dependencies"],
|
|
84
|
+
...pkg["devDependencies"],
|
|
85
|
+
};
|
|
86
|
+
const hasTypeScript = "typescript" in deps || "ts-node" in deps || "tsx" in deps;
|
|
87
|
+
const language = hasTypeScript ? "TypeScript" : "JavaScript";
|
|
88
|
+
let framework;
|
|
89
|
+
if ("next" in deps)
|
|
90
|
+
framework = "Next.js";
|
|
91
|
+
else if ("express" in deps)
|
|
92
|
+
framework = "Express";
|
|
93
|
+
else if ("fastify" in deps)
|
|
94
|
+
framework = "Fastify";
|
|
95
|
+
else if ("react" in deps)
|
|
96
|
+
framework = "React";
|
|
97
|
+
else if ("vue" in deps)
|
|
98
|
+
framework = "Vue";
|
|
99
|
+
else if ("@angular/core" in deps)
|
|
100
|
+
framework = "Angular";
|
|
101
|
+
else if ("svelte" in deps)
|
|
102
|
+
framework = "Svelte";
|
|
103
|
+
return {
|
|
104
|
+
language,
|
|
105
|
+
framework,
|
|
106
|
+
package_manager: "npm",
|
|
107
|
+
runtime: "Node.js",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return {
|
|
112
|
+
language: "JavaScript",
|
|
113
|
+
package_manager: "npm",
|
|
114
|
+
runtime: "Node.js",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
detectFromRequirementsTxt(content) {
|
|
119
|
+
let framework;
|
|
120
|
+
const lower = content.toLowerCase();
|
|
121
|
+
if (lower.includes("django"))
|
|
122
|
+
framework = "Django";
|
|
123
|
+
else if (lower.includes("flask"))
|
|
124
|
+
framework = "Flask";
|
|
125
|
+
else if (lower.includes("fastapi"))
|
|
126
|
+
framework = "FastAPI";
|
|
127
|
+
return {
|
|
128
|
+
language: "Python",
|
|
129
|
+
framework,
|
|
130
|
+
package_manager: "pip",
|
|
131
|
+
runtime: "Python",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
detectFromPyprojectToml(content) {
|
|
135
|
+
let framework;
|
|
136
|
+
const lower = content.toLowerCase();
|
|
137
|
+
if (lower.includes("django"))
|
|
138
|
+
framework = "Django";
|
|
139
|
+
else if (lower.includes("flask"))
|
|
140
|
+
framework = "Flask";
|
|
141
|
+
else if (lower.includes("fastapi"))
|
|
142
|
+
framework = "FastAPI";
|
|
143
|
+
const usesPoetry = lower.includes("[tool.poetry]");
|
|
144
|
+
return {
|
|
145
|
+
language: "Python",
|
|
146
|
+
framework,
|
|
147
|
+
package_manager: usesPoetry ? "poetry" : "pip",
|
|
148
|
+
runtime: "Python",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
detectFromGoMod(content) {
|
|
152
|
+
let framework;
|
|
153
|
+
if (content.includes("gin-gonic"))
|
|
154
|
+
framework = "Gin";
|
|
155
|
+
else if (content.includes("gorilla/mux"))
|
|
156
|
+
framework = "Gorilla Mux";
|
|
157
|
+
else if (content.includes("echo"))
|
|
158
|
+
framework = "Echo";
|
|
159
|
+
return {
|
|
160
|
+
language: "Go",
|
|
161
|
+
framework,
|
|
162
|
+
package_manager: "go modules",
|
|
163
|
+
runtime: "Go",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
countNodes(tree) {
|
|
167
|
+
let files = 0;
|
|
168
|
+
let dirs = 0;
|
|
169
|
+
if (tree.type === "file") {
|
|
170
|
+
files = 1;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
dirs = 1;
|
|
174
|
+
if (tree.children) {
|
|
175
|
+
for (const child of tree.children) {
|
|
176
|
+
const counts = this.countNodes(child);
|
|
177
|
+
files += counts.files;
|
|
178
|
+
dirs += counts.dirs;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return { files, dirs };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=codebase-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codebase-scanner.js","sourceRoot":"","sources":["../../src/services/codebase-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EarsValidator — EARS pattern detection and validation.
|
|
3
|
+
* Pure service with no disk dependencies.
|
|
4
|
+
*/
|
|
5
|
+
import type { EarsPatternName } from "../constants.js";
|
|
6
|
+
import type { ValidationResult, BatchValidationResult, EarsImprovement, EarsRequirement } from "../types.js";
|
|
7
|
+
export declare class EarsValidator {
|
|
8
|
+
/**
|
|
9
|
+
* Detect which EARS pattern a requirement follows.
|
|
10
|
+
*/
|
|
11
|
+
detectPattern(requirement: string): EarsPatternName;
|
|
12
|
+
/**
|
|
13
|
+
* Validate a single requirement against EARS patterns.
|
|
14
|
+
*/
|
|
15
|
+
validate(requirement: string): ValidationResult;
|
|
16
|
+
/**
|
|
17
|
+
* Suggest an EARS-compliant rewrite for a non-conforming requirement.
|
|
18
|
+
*/
|
|
19
|
+
suggestImprovement(requirement: string): EarsImprovement;
|
|
20
|
+
/**
|
|
21
|
+
* Validate multiple requirements at once.
|
|
22
|
+
*/
|
|
23
|
+
validateAll(requirements: EarsRequirement[]): BatchValidationResult;
|
|
24
|
+
/**
|
|
25
|
+
* Extract the action part from a vague requirement.
|
|
26
|
+
*/
|
|
27
|
+
private extractAction;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ears-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ears-validator.d.ts","sourceRoot":"","sources":["../../src/services/ears-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAgD7G,qBAAa,aAAa;IACxB;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;IAanD;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB;IAmC/C;;OAEG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;IAuCxD;;OAEG;IACH,WAAW,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,qBAAqB;IAkBnE;;OAEG;IACH,OAAO,CAAC,aAAa;CAgBtB"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EarsValidator — EARS pattern detection and validation.
|
|
3
|
+
* Pure service with no disk dependencies.
|
|
4
|
+
*/
|
|
5
|
+
const PATTERN_RULES = [
|
|
6
|
+
{
|
|
7
|
+
name: "event_driven",
|
|
8
|
+
regex: /^When\s+.+,\s+the\s+(system|server|tool)\s+shall\s+/i,
|
|
9
|
+
priority: 1,
|
|
10
|
+
template: 'When <trigger>, the system shall <response>.',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "state_driven",
|
|
14
|
+
regex: /^While\s+.+,\s+the\s+(system|server|tool)\s+shall\s+/i,
|
|
15
|
+
priority: 2,
|
|
16
|
+
template: 'While <state>, the system shall <behavior>.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "optional",
|
|
20
|
+
regex: /^Where\s+.+,\s+the\s+(system|server|tool)\s+shall\s+/i,
|
|
21
|
+
priority: 3,
|
|
22
|
+
template: 'Where <feature is included>, the system shall <behavior>.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "unwanted",
|
|
26
|
+
regex: /^If\s+.+,\s+then\s+the\s+(system|server|tool)\s+shall\s+/i,
|
|
27
|
+
priority: 4,
|
|
28
|
+
template: 'If <unwanted condition>, then the system shall <mitigation>.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "complex",
|
|
32
|
+
regex: /^(While\s+.+,\s+)?[Ww]hen\s+.+,\s+(if\s+.+,\s+)?the\s+(system|server|tool)\s+shall\s+/i,
|
|
33
|
+
priority: 5,
|
|
34
|
+
template: 'While <state>, when <trigger>, the system shall <response>.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "ubiquitous",
|
|
38
|
+
regex: /^The\s+(system|server|tool)\s+shall\s+/i,
|
|
39
|
+
priority: 6,
|
|
40
|
+
template: 'The system shall <behavior>.',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
export class EarsValidator {
|
|
44
|
+
/**
|
|
45
|
+
* Detect which EARS pattern a requirement follows.
|
|
46
|
+
*/
|
|
47
|
+
detectPattern(requirement) {
|
|
48
|
+
const trimmed = requirement.trim();
|
|
49
|
+
// Check patterns in priority order (most specific first)
|
|
50
|
+
for (const rule of PATTERN_RULES) {
|
|
51
|
+
if (rule.regex.test(trimmed)) {
|
|
52
|
+
return rule.name;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return "unknown";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate a single requirement against EARS patterns.
|
|
59
|
+
*/
|
|
60
|
+
validate(requirement) {
|
|
61
|
+
const pattern = this.detectPattern(requirement);
|
|
62
|
+
const issues = [];
|
|
63
|
+
if (pattern === "unknown") {
|
|
64
|
+
issues.push("Requirement does not match any EARS pattern.");
|
|
65
|
+
issues.push("EARS patterns start with: 'The system shall', 'When...', 'While...', 'Where...', or 'If..., then...'");
|
|
66
|
+
return {
|
|
67
|
+
valid: false,
|
|
68
|
+
pattern,
|
|
69
|
+
issues,
|
|
70
|
+
suggestion: this.suggestImprovement(requirement).suggestion,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Check for testability
|
|
74
|
+
if (requirement.length < 20) {
|
|
75
|
+
issues.push("Requirement is too short to be testable.");
|
|
76
|
+
}
|
|
77
|
+
// Check for vague terms
|
|
78
|
+
const vagueTerms = ["fast", "good", "easy", "nice", "better", "properly", "appropriate"];
|
|
79
|
+
for (const term of vagueTerms) {
|
|
80
|
+
if (requirement.toLowerCase().includes(term)) {
|
|
81
|
+
issues.push(`Contains vague term "${term}" — replace with measurable criteria.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
valid: issues.length === 0,
|
|
86
|
+
pattern,
|
|
87
|
+
issues: issues.length > 0 ? issues : undefined,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Suggest an EARS-compliant rewrite for a non-conforming requirement.
|
|
92
|
+
*/
|
|
93
|
+
suggestImprovement(requirement) {
|
|
94
|
+
const trimmed = requirement.trim().toLowerCase();
|
|
95
|
+
// Try to determine intent
|
|
96
|
+
if (trimmed.includes("when") || trimmed.includes("trigger") || trimmed.includes("event")) {
|
|
97
|
+
return {
|
|
98
|
+
pattern: "event_driven",
|
|
99
|
+
suggestion: `When <trigger event>, the system shall ${this.extractAction(requirement)}.`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (trimmed.includes("while") || trimmed.includes("during") || trimmed.includes("state")) {
|
|
103
|
+
return {
|
|
104
|
+
pattern: "state_driven",
|
|
105
|
+
suggestion: `While <system state>, the system shall ${this.extractAction(requirement)}.`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (trimmed.includes("if") || trimmed.includes("error") || trimmed.includes("fail")) {
|
|
109
|
+
return {
|
|
110
|
+
pattern: "unwanted",
|
|
111
|
+
suggestion: `If <unwanted condition>, then the system shall ${this.extractAction(requirement)}.`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (trimmed.includes("option") || trimmed.includes("where") || trimmed.includes("config")) {
|
|
115
|
+
return {
|
|
116
|
+
pattern: "optional",
|
|
117
|
+
suggestion: `Where <feature is enabled>, the system shall ${this.extractAction(requirement)}.`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Default to ubiquitous
|
|
121
|
+
return {
|
|
122
|
+
pattern: "ubiquitous",
|
|
123
|
+
suggestion: `The system shall ${this.extractAction(requirement)}.`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Validate multiple requirements at once.
|
|
128
|
+
*/
|
|
129
|
+
validateAll(requirements) {
|
|
130
|
+
const results = [];
|
|
131
|
+
let valid = 0;
|
|
132
|
+
let invalid = 0;
|
|
133
|
+
for (const req of requirements) {
|
|
134
|
+
const result = this.validate(req.text);
|
|
135
|
+
results.push(result);
|
|
136
|
+
if (result.valid) {
|
|
137
|
+
valid++;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
invalid++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { valid, invalid, results };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Extract the action part from a vague requirement.
|
|
147
|
+
*/
|
|
148
|
+
extractAction(requirement) {
|
|
149
|
+
// Remove common prefixes
|
|
150
|
+
let action = requirement.trim();
|
|
151
|
+
const prefixes = [
|
|
152
|
+
/^(the\s+)?(system|server|tool|app|application)\s+(should|must|will|needs?\s+to|has\s+to)\s+/i,
|
|
153
|
+
/^(make|ensure|do|create|implement|add|build)\s+/i,
|
|
154
|
+
];
|
|
155
|
+
for (const prefix of prefixes) {
|
|
156
|
+
action = action.replace(prefix, "");
|
|
157
|
+
}
|
|
158
|
+
// Clean up
|
|
159
|
+
action = action.replace(/\.$/, "").trim();
|
|
160
|
+
return action || "perform the specified action";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=ears-validator.js.map
|