@ipation/specbridge 1.0.2 → 1.0.4
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/CHANGELOG.md +165 -0
- package/dist/cli.js +53 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.js +47 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,171 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.4] - 2026-01-30
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
#### Critical Fixes
|
|
15
|
+
- **Verification Timeout Bug** (src/verification/engine.ts)
|
|
16
|
+
- Fixed event loop hanging issue where verification would wait for full 60-second timeout even after completing
|
|
17
|
+
- Added proper timeout handle cleanup with try-finally block
|
|
18
|
+
- Used `unref()` to prevent blocking process exit
|
|
19
|
+
- **Impact**: Integration tests now complete in ~97 seconds instead of hanging for 10+ minutes
|
|
20
|
+
- CLI verify command exits immediately (~600ms) instead of waiting 60 seconds
|
|
21
|
+
|
|
22
|
+
#### Inference Issues
|
|
23
|
+
- **Non-Existent Verifier References** (src/inference/analyzers/errors.ts)
|
|
24
|
+
- Fixed `error-hierarchy` verifier reference → `errors` (line 90)
|
|
25
|
+
- Fixed `custom-errors-only` verifier reference → `errors` (line 205)
|
|
26
|
+
- Updated corresponding unit tests to match
|
|
27
|
+
- **Impact**: Inferred constraints now reference valid verifiers that exist in the registry
|
|
28
|
+
|
|
29
|
+
#### CLI Command Issues
|
|
30
|
+
- **`infer --output` Bug** (src/cli/commands/infer.ts)
|
|
31
|
+
- Fixed early return preventing file save when no patterns detected
|
|
32
|
+
- Moved file saving logic before pattern check
|
|
33
|
+
- **Impact**: Output file is always created when `--output` flag is used, even with empty results
|
|
34
|
+
|
|
35
|
+
#### Visual Formatting
|
|
36
|
+
- **Empty Placeholder Characters** (src/reporting/formats/markdown.ts, src/agent/context.generator.ts)
|
|
37
|
+
- Added compliance emojis: ✅ (≥90%), ⚠️ (70-89%), ❌ (<70%)
|
|
38
|
+
- Added progress bar characters: █ (filled), ░ (empty)
|
|
39
|
+
- Added constraint type emojis: 🔒 (invariant), 📋 (convention), 💡 (guideline)
|
|
40
|
+
- **Impact**: Reports and agent context now display with proper visual indicators
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
#### Documentation
|
|
45
|
+
- **Version Consistency**
|
|
46
|
+
- Archived outdated `IMPLEMENTATION_STATUS.md` → `IMPLEMENTATION_STATUS_v0.2.2_ARCHIVED.md`
|
|
47
|
+
- Added archive notice indicating current version is 1.0.4
|
|
48
|
+
- **Impact**: No misleading version information
|
|
49
|
+
|
|
50
|
+
- **GitHub URL Standardization**
|
|
51
|
+
- Standardized all repository references to `nouatzi/specbridge`
|
|
52
|
+
- Fixed docs/README.md, docs/troubleshooting.md, src/reporting/formats/markdown.ts
|
|
53
|
+
- Corrected wrong `anthropics/claude-code` reference in archived file
|
|
54
|
+
- **Impact**: Consistent repository URLs throughout documentation and code
|
|
55
|
+
|
|
56
|
+
### Improved
|
|
57
|
+
|
|
58
|
+
#### Test Suite
|
|
59
|
+
- **Integration Tests** (tests/integration/cli.test.ts)
|
|
60
|
+
- Fixed `decision create` test syntax (positional argument instead of `--id` flag)
|
|
61
|
+
- Added valid constraints to manually created decision files (schema compliance)
|
|
62
|
+
- Fixed 3 previously failing CLI integration tests
|
|
63
|
+
- **Result**: All 30 integration tests now pass (100% success rate)
|
|
64
|
+
- **Duration**: Tests complete in ~97 seconds (down from 10+ minutes)
|
|
65
|
+
|
|
66
|
+
### Test Results Summary
|
|
67
|
+
- ✅ Type Checking: Passed
|
|
68
|
+
- ✅ Unit Tests: 762/762 passed (24 test files)
|
|
69
|
+
- ✅ Integration Tests: 30/30 passed (2 test files)
|
|
70
|
+
- ✅ Total Duration: ~97 seconds
|
|
71
|
+
|
|
72
|
+
### Files Modified
|
|
73
|
+
- 9 source/test files updated
|
|
74
|
+
- 2 documentation files updated
|
|
75
|
+
- 1 file archived
|
|
76
|
+
- All changes backward compatible (no breaking changes)
|
|
77
|
+
|
|
78
|
+
## [1.0.3] - 2026-01-30
|
|
79
|
+
|
|
80
|
+
### Improved
|
|
81
|
+
|
|
82
|
+
#### Test Coverage - Major Achievement 🎉
|
|
83
|
+
- **Overall coverage**: 67.37% → **94.07%** (+26.7% improvement, +39.6% total increase from v1.0.1)
|
|
84
|
+
- **Functions coverage**: 78.21% → **96.99%** (+18.78%, now at 97%)
|
|
85
|
+
- **Branches coverage**: 81.95% → **93.93%** (+11.98%, exceeds 87% target)
|
|
86
|
+
- **Statements coverage**: 67.37% → **94.07%** (+26.7%, exceeds 83% target)
|
|
87
|
+
- **Total tests**: 312 → **762** (+450 tests, +144% growth)
|
|
88
|
+
|
|
89
|
+
**All coverage targets exceeded by 7-14 percentage points!**
|
|
90
|
+
|
|
91
|
+
#### Sprint 1: Foundation Components (New Tests)
|
|
92
|
+
- **Scanner Tests** - Added `scanner.test.ts`: 580 lines, 48 tests
|
|
93
|
+
- Coverage: 0% → **99.38%** (was completely untested)
|
|
94
|
+
- Tests all 8 public methods with comprehensive edge cases
|
|
95
|
+
- Handles malformed files, nested structures, large file sets
|
|
96
|
+
|
|
97
|
+
- **Propagation Graph Tests** - Added `graph.test.ts`: 480 lines, 34 tests
|
|
98
|
+
- Coverage: 0% → **100%** (was completely untested)
|
|
99
|
+
- Tests dependency graph building, transitive dependencies
|
|
100
|
+
- Edge cases: cycles, empty graphs, overlapping scopes
|
|
101
|
+
|
|
102
|
+
- **Core Error Classes Tests** - Added `errors.test.ts`: 400 lines
|
|
103
|
+
- All 11 error classes comprehensively tested
|
|
104
|
+
- formatError() function validated with edge cases
|
|
105
|
+
- Error inheritance chain and special character handling
|
|
106
|
+
|
|
107
|
+
- **Verification Engine** - Extended `engine.test.ts`: +280 lines
|
|
108
|
+
- Added timeout handling, severity filtering, exception lifecycle
|
|
109
|
+
- Edge cases: empty sourceRoots, concurrent calls, malformed files
|
|
110
|
+
- Coverage improved with comprehensive scenario testing
|
|
111
|
+
|
|
112
|
+
- **Registry** - Extended `registry.test.ts`: +290 lines
|
|
113
|
+
- Filter combinations (status+tags, constraintType+severity)
|
|
114
|
+
- Additional methods: has(), getIds(), getByOwner(), getByTag()
|
|
115
|
+
- Coverage: **100%** (maintained and enhanced)
|
|
116
|
+
|
|
117
|
+
- **Reporter** - Extended `reporter.test.ts`: +320 lines
|
|
118
|
+
- formatAsTableGrouped(), formatAsMarkdown() comprehensive tests
|
|
119
|
+
- checkDegradation() function fully tested
|
|
120
|
+
- generateComplianceReport() edge cases covered
|
|
121
|
+
|
|
122
|
+
#### Sprint 2: Partial Coverage Improvements
|
|
123
|
+
- **Agent Context Generator** - Extended `context.generator.test.ts`: +400 lines, 42 total tests
|
|
124
|
+
- Coverage: 43.52% → **98.96%** (+55.44%, highest improvement)
|
|
125
|
+
- Standalone functions fully tested: generateContext, formatContextAsMarkdown, formatContextAsJson, formatContextAsMcp
|
|
126
|
+
- Format conversion validation (markdown/JSON/MCP)
|
|
127
|
+
- Rationale inclusion/exclusion scenarios
|
|
128
|
+
|
|
129
|
+
- **Propagation Engine** - Extended `engine.test.ts`: +200 lines, 29 total tests
|
|
130
|
+
- Coverage: 62.5% → **76.92%** (+14.42%)
|
|
131
|
+
- Migration step generation and prioritization tested
|
|
132
|
+
- Effort estimation with various scenarios
|
|
133
|
+
- Auto-fix detection and affected files handling
|
|
134
|
+
|
|
135
|
+
- **Registry Loader** - Extended `loader.test.ts`: +200 lines, 11 new tests
|
|
136
|
+
- Coverage: 67.21% → **100%** (+32.79%)
|
|
137
|
+
- validateDecisionFile() function completely tested
|
|
138
|
+
- Schema validation, corrupted file handling
|
|
139
|
+
- All error scenarios validated
|
|
140
|
+
|
|
141
|
+
#### Components at Perfect Coverage (100%)
|
|
142
|
+
- ✅ Registry (100%)
|
|
143
|
+
- ✅ Registry Loader (100%)
|
|
144
|
+
- ✅ Propagation Graph (100%)
|
|
145
|
+
- ✅ Analyzers (100%)
|
|
146
|
+
- ✅ Config Schemas (100%)
|
|
147
|
+
- ✅ Utils (100%)
|
|
148
|
+
|
|
149
|
+
#### Components at Excellent Coverage (95-99%)
|
|
150
|
+
- ✅ Agent Context Generator (98.96%)
|
|
151
|
+
- ✅ Scanner (99.38%)
|
|
152
|
+
- ✅ Verifiers (99.72%)
|
|
153
|
+
- ✅ Functions (96.99%)
|
|
154
|
+
|
|
155
|
+
#### Quality Metrics
|
|
156
|
+
- ✅ **762 tests passing** (100% pass rate, +450 tests)
|
|
157
|
+
- ✅ **94.07% test coverage** (exceeds all thresholds)
|
|
158
|
+
- ✅ **No type errors**
|
|
159
|
+
- ✅ **Build succeeds**
|
|
160
|
+
- ✅ **Test-to-source ratio**: ~1.7:1
|
|
161
|
+
- ✅ **Production-ready quality**
|
|
162
|
+
|
|
163
|
+
### Changed
|
|
164
|
+
- Updated coverage thresholds in `vitest.config.ts`:
|
|
165
|
+
- Lines: 67% → **83%**
|
|
166
|
+
- Functions: 78% → **83%**
|
|
167
|
+
- Branches: 78% → **87%**
|
|
168
|
+
- Statements: 67% → **83%**
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
- Comprehensive test helpers and fixtures for easier test creation
|
|
172
|
+
- Integration test scenarios for multi-command workflows
|
|
173
|
+
- Edge case testing for all critical components
|
|
174
|
+
|
|
10
175
|
## [1.0.2] - 2026-01-29
|
|
11
176
|
|
|
12
177
|
### Improved
|
package/dist/cli.js
CHANGED
|
@@ -1005,9 +1005,7 @@ var ErrorsAnalyzer = class {
|
|
|
1005
1005
|
);
|
|
1006
1006
|
if (errorClasses.length < 2) return null;
|
|
1007
1007
|
const files = scanner.getFiles();
|
|
1008
|
-
|
|
1009
|
-
let extendsCustomBase = 0;
|
|
1010
|
-
let customBaseName = null;
|
|
1008
|
+
const baseCount = /* @__PURE__ */ new Map();
|
|
1011
1009
|
for (const errorClass of errorClasses) {
|
|
1012
1010
|
const file = files.find((f) => f.path === errorClass.file);
|
|
1013
1011
|
if (!file) continue;
|
|
@@ -1016,22 +1014,27 @@ var ErrorsAnalyzer = class {
|
|
|
1016
1014
|
const extendClause = classDecl.getExtends();
|
|
1017
1015
|
if (extendClause) {
|
|
1018
1016
|
const baseName = extendClause.getText();
|
|
1019
|
-
if (baseName
|
|
1020
|
-
|
|
1021
|
-
} else if (baseName.endsWith("Error")) {
|
|
1022
|
-
extendsCustomBase++;
|
|
1023
|
-
customBaseName = customBaseName || baseName;
|
|
1017
|
+
if (baseName !== "Error" && baseName.endsWith("Error")) {
|
|
1018
|
+
baseCount.set(baseName, (baseCount.get(baseName) || 0) + 1);
|
|
1024
1019
|
}
|
|
1025
1020
|
}
|
|
1026
1021
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1022
|
+
let customBaseName = null;
|
|
1023
|
+
let maxCount = 0;
|
|
1024
|
+
for (const [baseName, count] of baseCount.entries()) {
|
|
1025
|
+
if (count > maxCount) {
|
|
1026
|
+
maxCount = count;
|
|
1027
|
+
customBaseName = baseName;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (maxCount >= 3 && customBaseName) {
|
|
1031
|
+
const confidence = calculateConfidence(maxCount, errorClasses.length);
|
|
1029
1032
|
return createPattern(this.id, {
|
|
1030
1033
|
id: "errors-custom-base",
|
|
1031
1034
|
name: "Custom Error Base Class",
|
|
1032
1035
|
description: `Custom errors extend a common base class (${customBaseName})`,
|
|
1033
1036
|
confidence,
|
|
1034
|
-
occurrences:
|
|
1037
|
+
occurrences: maxCount,
|
|
1035
1038
|
examples: errorClasses.slice(0, 3).map((c) => ({
|
|
1036
1039
|
file: c.file,
|
|
1037
1040
|
line: c.line,
|
|
@@ -1042,7 +1045,7 @@ var ErrorsAnalyzer = class {
|
|
|
1042
1045
|
rule: `Custom error classes should extend ${customBaseName}`,
|
|
1043
1046
|
severity: "medium",
|
|
1044
1047
|
scope: "src/**/*.ts",
|
|
1045
|
-
verifier: "
|
|
1048
|
+
verifier: "errors"
|
|
1046
1049
|
}
|
|
1047
1050
|
});
|
|
1048
1051
|
}
|
|
@@ -1113,10 +1116,11 @@ var ErrorsAnalyzer = class {
|
|
|
1113
1116
|
} else if (text.startsWith("new ") && text.includes("Error")) {
|
|
1114
1117
|
throwCustom++;
|
|
1115
1118
|
if (examples.length < 3) {
|
|
1119
|
+
const snippet = text.length > 50 ? text.slice(0, 50) + "..." : text;
|
|
1116
1120
|
examples.push({
|
|
1117
1121
|
file: path,
|
|
1118
1122
|
line: node.getStartLineNumber(),
|
|
1119
|
-
snippet: `throw ${
|
|
1123
|
+
snippet: `throw ${snippet}`
|
|
1120
1124
|
});
|
|
1121
1125
|
}
|
|
1122
1126
|
}
|
|
@@ -1138,7 +1142,7 @@ var ErrorsAnalyzer = class {
|
|
|
1138
1142
|
rule: "Throw custom error classes instead of generic Error",
|
|
1139
1143
|
severity: "medium",
|
|
1140
1144
|
scope: "src/**/*.ts",
|
|
1141
|
-
verifier: "
|
|
1145
|
+
verifier: "errors"
|
|
1142
1146
|
}
|
|
1143
1147
|
});
|
|
1144
1148
|
}
|
|
@@ -1277,6 +1281,12 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
|
|
|
1277
1281
|
cwd
|
|
1278
1282
|
});
|
|
1279
1283
|
spinner.succeed(`Scanned ${result.filesScanned} files in ${result.duration}ms`);
|
|
1284
|
+
if (options.save || options.output) {
|
|
1285
|
+
const outputPath = options.output || join3(getInferredDir(cwd), "patterns.json");
|
|
1286
|
+
await writeTextFile(outputPath, JSON.stringify(result, null, 2));
|
|
1287
|
+
console.log(chalk2.green(`
|
|
1288
|
+
Results saved to: ${outputPath}`));
|
|
1289
|
+
}
|
|
1280
1290
|
if (result.patterns.length === 0) {
|
|
1281
1291
|
console.log(chalk2.yellow("\nNo patterns detected above confidence threshold."));
|
|
1282
1292
|
console.log(chalk2.dim(`Try lowering --min-confidence (current: ${minConfidence})`));
|
|
@@ -1287,12 +1297,6 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
|
|
|
1287
1297
|
} else {
|
|
1288
1298
|
printPatterns(result.patterns);
|
|
1289
1299
|
}
|
|
1290
|
-
if (options.save || options.output) {
|
|
1291
|
-
const outputPath = options.output || join3(getInferredDir(cwd), "patterns.json");
|
|
1292
|
-
await writeTextFile(outputPath, JSON.stringify(result, null, 2));
|
|
1293
|
-
console.log(chalk2.green(`
|
|
1294
|
-
Results saved to: ${outputPath}`));
|
|
1295
|
-
}
|
|
1296
1300
|
if (!options.json) {
|
|
1297
1301
|
console.log("");
|
|
1298
1302
|
console.log(chalk2.cyan("Next steps:"));
|
|
@@ -2124,9 +2128,11 @@ var VerificationEngine = class {
|
|
|
2124
2128
|
let passed = 0;
|
|
2125
2129
|
let failed = 0;
|
|
2126
2130
|
const skipped = 0;
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2131
|
+
let timeoutHandle = null;
|
|
2132
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
2133
|
+
timeoutHandle = setTimeout(() => resolve("timeout"), timeout);
|
|
2134
|
+
timeoutHandle.unref();
|
|
2135
|
+
});
|
|
2130
2136
|
const verificationPromise = this.verifyFiles(
|
|
2131
2137
|
filesToVerify,
|
|
2132
2138
|
decisions,
|
|
@@ -2142,17 +2148,25 @@ var VerificationEngine = class {
|
|
|
2142
2148
|
}
|
|
2143
2149
|
}
|
|
2144
2150
|
);
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2151
|
+
let result;
|
|
2152
|
+
try {
|
|
2153
|
+
result = await Promise.race([verificationPromise, timeoutPromise]);
|
|
2154
|
+
if (result === "timeout") {
|
|
2155
|
+
return {
|
|
2156
|
+
success: false,
|
|
2157
|
+
violations: allViolations,
|
|
2158
|
+
checked,
|
|
2159
|
+
passed,
|
|
2160
|
+
failed,
|
|
2161
|
+
skipped: filesToVerify.length - checked,
|
|
2162
|
+
duration: timeout
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
} finally {
|
|
2166
|
+
if (timeoutHandle) {
|
|
2167
|
+
clearTimeout(timeoutHandle);
|
|
2168
|
+
timeoutHandle = null;
|
|
2169
|
+
}
|
|
2156
2170
|
}
|
|
2157
2171
|
const hasBlockingViolations = allViolations.some((v) => {
|
|
2158
2172
|
if (level === "commit") {
|
|
@@ -3097,7 +3111,7 @@ function formatMarkdownReport(report) {
|
|
|
3097
3111
|
lines.push("| Decision | Status | Constraints | Violations | Compliance |");
|
|
3098
3112
|
lines.push("|----------|--------|-------------|------------|------------|");
|
|
3099
3113
|
for (const dec of report.byDecision) {
|
|
3100
|
-
const complianceEmoji = dec.compliance >= 90 ? "" : dec.compliance >= 70 ? "" : "";
|
|
3114
|
+
const complianceEmoji = dec.compliance >= 90 ? "\u2705" : dec.compliance >= 70 ? "\u26A0\uFE0F" : "\u274C";
|
|
3101
3115
|
lines.push(
|
|
3102
3116
|
`| ${dec.title} | ${dec.status} | ${dec.constraints} | ${dec.violations} | ${complianceEmoji} ${dec.compliance}% |`
|
|
3103
3117
|
);
|
|
@@ -3106,15 +3120,15 @@ function formatMarkdownReport(report) {
|
|
|
3106
3120
|
}
|
|
3107
3121
|
lines.push("---");
|
|
3108
3122
|
lines.push("");
|
|
3109
|
-
lines.push("*Generated by [SpecBridge](https://github.com/
|
|
3123
|
+
lines.push("*Generated by [SpecBridge](https://github.com/nouatzi/specbridge)*");
|
|
3110
3124
|
return lines.join("\n");
|
|
3111
3125
|
}
|
|
3112
3126
|
function formatProgressBar(percentage) {
|
|
3113
3127
|
const width = 20;
|
|
3114
3128
|
const filled = Math.round(percentage / 100 * width);
|
|
3115
3129
|
const empty = width - filled;
|
|
3116
|
-
const filledChar = "";
|
|
3117
|
-
const emptyChar = "";
|
|
3130
|
+
const filledChar = "\u2588";
|
|
3131
|
+
const emptyChar = "\u2591";
|
|
3118
3132
|
return `\`${filledChar.repeat(filled)}${emptyChar.repeat(empty)}\` ${percentage}%`;
|
|
3119
3133
|
}
|
|
3120
3134
|
|
|
@@ -3232,7 +3246,7 @@ function formatContextAsMarkdown(context) {
|
|
|
3232
3246
|
lines.push("### Constraints");
|
|
3233
3247
|
lines.push("");
|
|
3234
3248
|
for (const constraint of decision.constraints) {
|
|
3235
|
-
const typeEmoji = constraint.type === "invariant" ? "" : constraint.type === "convention" ? "" : "";
|
|
3249
|
+
const typeEmoji = constraint.type === "invariant" ? "\u{1F512}" : constraint.type === "convention" ? "\u{1F4CB}" : "\u{1F4A1}";
|
|
3236
3250
|
const severityBadge = `[${constraint.severity.toUpperCase()}]`;
|
|
3237
3251
|
lines.push(`- ${typeEmoji} **${severityBadge}** ${constraint.rule}`);
|
|
3238
3252
|
}
|