speckeeper 0.8.0 → 0.8.1
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 +71 -16
- package/dist/cli.js +10 -4
- package/dist/cli.js.map +1 -1
- package/dist/{config-api-Bh0zX8W7.d.ts → config-api-U2pt1aHJ.d.ts} +3 -0
- package/dist/dsl/index.d.ts +1 -1
- package/dist/dsl/index.js +7 -2
- package/dist/dsl/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,10 +90,9 @@ npx speckeeper scaffold --source requirements.md
|
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
This generates:
|
|
93
|
-
- `design/_models/` — Model classes with base schema
|
|
93
|
+
- `design/_models/` — Model classes with base schema, lint rules, and `annotationChecker` bindings derived from `implements`/`verifiedBy` edges
|
|
94
94
|
- `design/*.ts` — Spec data files using `defineSpecs()`
|
|
95
95
|
- `design/index.ts` — Entry point via `mergeSpecs()`
|
|
96
|
-
- Checker bindings from `implements`/`verifiedBy` edges are added as guidance comments
|
|
97
96
|
|
|
98
97
|
See [Scaffold Mermaid Specification](./docs/scaffold-mermaid-spec.md) for the full input format.
|
|
99
98
|
|
|
@@ -152,7 +151,7 @@ npx speckeeper impact FR-001
|
|
|
152
151
|
| `speckeeper check` | Verify consistency with external SSOT |
|
|
153
152
|
| `speckeeper check test --coverage` | Verify test coverage for requirements |
|
|
154
153
|
| `speckeeper scaffold` | Generate model skeletons from a mermaid flowchart |
|
|
155
|
-
| `speckeeper drift` | Detect manual edits to generated `
|
|
154
|
+
| `speckeeper drift` | Detect manual edits to generated `docs/` files |
|
|
156
155
|
| `speckeeper impact <id>` | Analyze change impact for a specific element |
|
|
157
156
|
|
|
158
157
|
**Note**: `speckeeper build` generates machine-readable `specs/` output. For human-readable docs (`docs/`), use [embedoc](https://www.npmjs.com/package/embedoc) or similar tools with the model rendering API.
|
|
@@ -184,25 +183,77 @@ Checks include:
|
|
|
184
183
|
|
|
185
184
|
### External SSOT Validation (check)
|
|
186
185
|
|
|
187
|
-
Validate your specifications against actual implementation artifacts. speckeeper
|
|
186
|
+
Validate your specifications against actual implementation artifacts. speckeeper scans source and test files for **annotation comments** (`@verifies`, `@implements`, `@traces`) to automatically detect which specs are covered — no manual relation wiring needed.
|
|
188
187
|
|
|
189
|
-
|
|
190
|
-
|---------|--------|-----------|
|
|
191
|
-
| `testChecker()` | Test code | Test file existence + spec ID references in describe/it/test blocks |
|
|
192
|
-
| `externalOpenAPIChecker()` | OpenAPI spec | Spec ID existence (operationId, path, schema, x-spec-id), HTTP method, parameter/response property names and types |
|
|
193
|
-
| `externalSqlSchemaChecker()` | SQL schema | Table existence, column existence, type containment (DDL type must be equal or wider than spec type) |
|
|
194
|
-
| `relationCoverage()` | Cross-model | Coverage of a target model via relations |
|
|
188
|
+
#### Annotation-based auto-detection
|
|
195
189
|
|
|
196
|
-
|
|
190
|
+
Add annotations to your source and test files:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// tests/unit/auth.test.ts
|
|
194
|
+
// @verifies FR-001, FR-001-01
|
|
195
|
+
describe('User Authentication', () => { ... });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// src/auth/handler.ts
|
|
200
|
+
// @implements FR-001
|
|
201
|
+
export class AuthHandler { ... }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
speckeeper scans for these annotations and automatically links specs to their implementation and tests. Annotations work in any comment style (`//`, `#`, `--`, `/* */`, `<!-- -->`).
|
|
205
|
+
|
|
206
|
+
#### Artifact configuration
|
|
207
|
+
|
|
208
|
+
Define scan targets per artifact class in your config:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// speckeeper.config.ts
|
|
212
|
+
export default defineConfig({
|
|
213
|
+
// ...
|
|
214
|
+
artifacts: {
|
|
215
|
+
test: {
|
|
216
|
+
globs: ['test/**/*.test.ts', 'tests/**/*.test.ts'],
|
|
217
|
+
contentPatterns: [/@verifies\s+([\w-]+(?:[,\s]+[\w-]+)*)/],
|
|
218
|
+
},
|
|
219
|
+
typescript: {
|
|
220
|
+
globs: ['src/**/*.ts'],
|
|
221
|
+
exclude: ['src/**/*.test.ts', 'src/**/*.d.ts'],
|
|
222
|
+
contentPatterns: [/@implements\s+([\w-]+(?:[,\s]+[\w-]+)*)/],
|
|
223
|
+
},
|
|
224
|
+
openapi: {
|
|
225
|
+
globs: ['api/openapi.yaml'],
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Scaffold auto-generates this config based on your Mermaid flowchart's external nodes and their artifact classes.
|
|
232
|
+
|
|
233
|
+
#### Checker factories
|
|
234
|
+
|
|
235
|
+
| Factory | Type | Validates |
|
|
236
|
+
|---------|------|-----------|
|
|
237
|
+
| `annotationChecker()` | Generic | Scans files for `@verifies`/`@implements`/`@traces` annotations matching spec IDs |
|
|
238
|
+
| `annotationCoverage()` | Coverage | Computes coverage from annotation scan results (no manual relations needed) |
|
|
239
|
+
| `externalOpenAPIChecker()` | Specialized | OpenAPI spec: operationId, path, schema, x-spec-id, HTTP method, parameter/response types |
|
|
240
|
+
| `externalSqlSchemaChecker()` | Specialized | SQL schema: table existence, column existence, type containment |
|
|
241
|
+
|
|
242
|
+
`annotationChecker()` is the generic checker. Specialized checkers (`externalOpenAPIChecker`, `externalSqlSchemaChecker`) extend it with source-level parsing for deeper validation. Scaffold generates the appropriate checker binding based on your artifact classes.
|
|
197
243
|
|
|
198
244
|
```typescript
|
|
199
245
|
// design/_models/requirement.ts
|
|
200
|
-
import {
|
|
246
|
+
import { annotationChecker } from 'speckeeper/dsl';
|
|
201
247
|
|
|
202
248
|
class RequirementModel extends Model<typeof RequirementSchema> {
|
|
203
249
|
// ... schema, lintRules, etc.
|
|
204
250
|
|
|
205
|
-
protected externalChecker =
|
|
251
|
+
protected externalChecker = annotationChecker<Requirement>({
|
|
252
|
+
checks: [
|
|
253
|
+
{ artifact: 'test', relationType: 'verifiedBy' },
|
|
254
|
+
{ artifact: 'typescript', relationType: 'implements' },
|
|
255
|
+
],
|
|
256
|
+
});
|
|
206
257
|
}
|
|
207
258
|
```
|
|
208
259
|
|
|
@@ -214,10 +265,14 @@ speckeeper check
|
|
|
214
265
|
Design: design/
|
|
215
266
|
Type: test
|
|
216
267
|
|
|
268
|
+
Scanning artifacts...
|
|
269
|
+
test: 12 @verifies annotations in 8 files
|
|
270
|
+
typescript: 5 @implements annotations in 4 files
|
|
271
|
+
|
|
217
272
|
✓ All checks passed
|
|
218
273
|
|
|
219
|
-
Coverage: Requirement →
|
|
220
|
-
Coverage: 100% (7/7
|
|
274
|
+
Coverage: Requirement (verifiedBy → test)
|
|
275
|
+
Coverage: 100% (7/7 requirements verified)
|
|
221
276
|
```
|
|
222
277
|
|
|
223
278
|
You can also implement custom checkers for any external source by defining an `ExternalChecker<T>` directly. See [Model Definition Guide](./docs/model-guide.md) for details.
|
|
@@ -299,7 +354,7 @@ class RunbookModel extends Model<typeof RunbookSchema> {
|
|
|
299
354
|
}
|
|
300
355
|
```
|
|
301
356
|
|
|
302
|
-
Core DSL factories (`speckeeper/dsl`) include `requireField`, `arrayMinLength`, `idFormat`, `childIdFormat`, `markdownExporter`, `
|
|
357
|
+
Core DSL factories (`speckeeper/dsl`) include `requireField`, `arrayMinLength`, `idFormat`, `childIdFormat`, `markdownExporter`, `annotationChecker`, `annotationCoverage`, `externalOpenAPIChecker`, `externalSqlSchemaChecker`, `relationCoverage`, and `baseSpecSchema`.
|
|
303
358
|
|
|
304
359
|
## Documentation
|
|
305
360
|
|
package/dist/cli.js
CHANGED
|
@@ -196,7 +196,9 @@ async function buildCommand(options) {
|
|
|
196
196
|
}
|
|
197
197
|
for (const exporter of exporters) {
|
|
198
198
|
const outputDir = exporter.outputDir ? join(cwd, config.docsDir, exporter.outputDir) : join(cwd, config.docsDir);
|
|
199
|
-
|
|
199
|
+
if (exporter.single) {
|
|
200
|
+
ensureDir(outputDir);
|
|
201
|
+
}
|
|
200
202
|
if (exporter.single) {
|
|
201
203
|
for (const spec of modelSpecs) {
|
|
202
204
|
const content = exporter.single(spec);
|
|
@@ -209,8 +211,12 @@ async function buildCommand(options) {
|
|
|
209
211
|
}
|
|
210
212
|
if (exporter.index) {
|
|
211
213
|
const indexContent = exporter.index(modelSpecs);
|
|
214
|
+
const indexPath = exporter.outputFile ? join(cwd, config.docsDir, exporter.outputFile) : join(outputDir, "index.md");
|
|
215
|
+
if (!exporter.outputFile) {
|
|
216
|
+
ensureDir(outputDir);
|
|
217
|
+
}
|
|
212
218
|
files.push({
|
|
213
|
-
path:
|
|
219
|
+
path: indexPath,
|
|
214
220
|
content: indexContent
|
|
215
221
|
});
|
|
216
222
|
}
|
|
@@ -351,7 +357,7 @@ async function driftCommand(options) {
|
|
|
351
357
|
}
|
|
352
358
|
}
|
|
353
359
|
if (exporter.index) {
|
|
354
|
-
const indexPath = join(outputDir, "index.md");
|
|
360
|
+
const indexPath = exporter.outputFile ? join(cwd, config.docsDir, exporter.outputFile) : join(outputDir, "index.md");
|
|
355
361
|
if (!existsSync(indexPath)) {
|
|
356
362
|
results.push({ file: indexPath, status: "missing" });
|
|
357
363
|
} else {
|
|
@@ -1648,7 +1654,7 @@ var program = new Command();
|
|
|
1648
1654
|
program.name("speckeeper").description("Requirements and design management framework with TypeScript DSL").version(getVersion());
|
|
1649
1655
|
program.command("build").description("Generate docs/ and specs/ from TypeScript models").option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Base output directory path", ".").option("-f, --format <format>", "Output format: markdown, json, both", "both").option("-w, --watch", "Watch for changes and auto-regenerate").option("-v, --verbose", "Show detailed output").action(buildCommand);
|
|
1650
1656
|
program.command("lint").description("Check design integrity (ID duplicates, references, layer violations, etc.)").option("-c, --config <path>", "Path to config file").option("-p, --phase <phase>", "Phase gate to check against (REQ, HLD, LLD, OPS)").option("-s, --strict", "Treat warnings as errors").option("--fix", "Attempt to fix auto-fixable issues").option("-f, --format <format>", "Output format: text, json, github", "text").action(lintCommand);
|
|
1651
|
-
program.command("drift").description("Check if generated files have been manually edited").option("-c, --config <path>", "Path to config file").option("-u, --update", "Auto-update if differences are found").option("-f, --format <format>", "Output format: text, json, diff", "text").action(driftCommand);
|
|
1657
|
+
program.command("drift").description("Check if generated files have been manually edited").option("-c, --config <path>", "Path to config file").option("-u, --update", "Auto-update if differences are found").option("-f, --format <format>", "Output format: text, json, diff", "text").option("--fail-on-drift", "Exit with code 1 if drift is detected (for CI)").action(driftCommand);
|
|
1652
1658
|
program.command("check").description("Check external SSOT conformance (including custom models)").argument("[type]", "Type of check: external-ssot, openapi, ddl, iac, custom, all, test").option("-c, --config <path>", "Path to config file").option("--strict", "Treat warnings as errors").option("-v, --verbose", "Show detailed output").option("--coverage", "Check if all testable acceptance criteria are covered by TestRefs").action(checkCommand);
|
|
1653
1659
|
program.command("new").description("Create a new element with auto-generated ID").argument("<type>", "Type: requirement, usecase, entity, component, screen, flow, error-case, term").option("-k, --kind <kind>", "Sub-kind (e.g., functional, non-functional for requirements)").option("-n, --name <name>", "Name of the element").option("-o, --output <path>", "Output directory path").option("-t, --template <path>", "Path to template file").action(newCommand);
|
|
1654
1660
|
program.command("impact").description("Analyze impact of changes to an ID").argument("<id>", "ID to analyze (e.g., REQ-001, ENT-ORDER)").option("-c, --config <path>", "Path to config file").option("-d, --depth <depth>", "Analysis depth (reference tracking level)", "3").option("--direction <direction>", "Analysis direction: upstream, downstream, both", "both").option("-f, --format <format>", "Output format: text, json, mermaid", "text").action(impactCommand);
|