@ipation/specbridge 1.0.3 → 1.0.5
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 +68 -0
- package/dist/cli.js +154 -140
- package/dist/cli.js.map +1 -1
- package/dist/index.js +31 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,74 @@ 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
|
+
|
|
10
78
|
## [1.0.3] - 2026-01-30
|
|
11
79
|
|
|
12
80
|
### Improved
|
package/dist/cli.js
CHANGED
|
@@ -1045,7 +1045,7 @@ var ErrorsAnalyzer = class {
|
|
|
1045
1045
|
rule: `Custom error classes should extend ${customBaseName}`,
|
|
1046
1046
|
severity: "medium",
|
|
1047
1047
|
scope: "src/**/*.ts",
|
|
1048
|
-
verifier: "
|
|
1048
|
+
verifier: "errors"
|
|
1049
1049
|
}
|
|
1050
1050
|
});
|
|
1051
1051
|
}
|
|
@@ -1142,7 +1142,7 @@ var ErrorsAnalyzer = class {
|
|
|
1142
1142
|
rule: "Throw custom error classes instead of generic Error",
|
|
1143
1143
|
severity: "medium",
|
|
1144
1144
|
scope: "src/**/*.ts",
|
|
1145
|
-
verifier: "
|
|
1145
|
+
verifier: "errors"
|
|
1146
1146
|
}
|
|
1147
1147
|
});
|
|
1148
1148
|
}
|
|
@@ -1281,6 +1281,12 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
|
|
|
1281
1281
|
cwd
|
|
1282
1282
|
});
|
|
1283
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
|
+
}
|
|
1284
1290
|
if (result.patterns.length === 0) {
|
|
1285
1291
|
console.log(chalk2.yellow("\nNo patterns detected above confidence threshold."));
|
|
1286
1292
|
console.log(chalk2.dim(`Try lowering --min-confidence (current: ${minConfidence})`));
|
|
@@ -1291,12 +1297,6 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
|
|
|
1291
1297
|
} else {
|
|
1292
1298
|
printPatterns(result.patterns);
|
|
1293
1299
|
}
|
|
1294
|
-
if (options.save || options.output) {
|
|
1295
|
-
const outputPath = options.output || join3(getInferredDir(cwd), "patterns.json");
|
|
1296
|
-
await writeTextFile(outputPath, JSON.stringify(result, null, 2));
|
|
1297
|
-
console.log(chalk2.green(`
|
|
1298
|
-
Results saved to: ${outputPath}`));
|
|
1299
|
-
}
|
|
1300
1300
|
if (!options.json) {
|
|
1301
1301
|
console.log("");
|
|
1302
1302
|
console.log(chalk2.cyan("Next steps:"));
|
|
@@ -2128,9 +2128,11 @@ var VerificationEngine = class {
|
|
|
2128
2128
|
let passed = 0;
|
|
2129
2129
|
let failed = 0;
|
|
2130
2130
|
const skipped = 0;
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2131
|
+
let timeoutHandle = null;
|
|
2132
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
2133
|
+
timeoutHandle = setTimeout(() => resolve("timeout"), timeout);
|
|
2134
|
+
timeoutHandle.unref();
|
|
2135
|
+
});
|
|
2134
2136
|
const verificationPromise = this.verifyFiles(
|
|
2135
2137
|
filesToVerify,
|
|
2136
2138
|
decisions,
|
|
@@ -2146,17 +2148,25 @@ var VerificationEngine = class {
|
|
|
2146
2148
|
}
|
|
2147
2149
|
}
|
|
2148
2150
|
);
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
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
|
+
}
|
|
2160
2170
|
}
|
|
2161
2171
|
const hasBlockingViolations = allViolations.some((v) => {
|
|
2162
2172
|
if (level === "commit") {
|
|
@@ -2752,38 +2762,22 @@ npx specbridge hook run --level commit --files "$STAGED_FILES"
|
|
|
2752
2762
|
|
|
2753
2763
|
exit $?
|
|
2754
2764
|
`;
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
hookContent = HOOK_SCRIPT;
|
|
2768
|
-
spinner.text = "Installing husky pre-commit hook...";
|
|
2769
|
-
} else if (options.lefthook) {
|
|
2770
|
-
spinner.succeed("Lefthook detected");
|
|
2771
|
-
console.log("");
|
|
2772
|
-
console.log(chalk8.cyan("Add this to your lefthook.yml:"));
|
|
2773
|
-
console.log("");
|
|
2774
|
-
console.log(chalk8.dim(`pre-commit:
|
|
2775
|
-
commands:
|
|
2776
|
-
specbridge:
|
|
2777
|
-
glob: "*.{ts,tsx}"
|
|
2778
|
-
run: npx specbridge hook run --level commit --files {staged_files}
|
|
2779
|
-
`));
|
|
2780
|
-
return;
|
|
2781
|
-
} else {
|
|
2782
|
-
if (await pathExists(join7(cwd, ".husky"))) {
|
|
2765
|
+
function createHookCommand() {
|
|
2766
|
+
const hookCommand2 = new Command9("hook").description("Manage Git hooks for verification");
|
|
2767
|
+
hookCommand2.command("install").description("Install Git pre-commit hook").option("-f, --force", "Overwrite existing hook").option("--husky", "Install for husky").option("--lefthook", "Install for lefthook").action(async (options) => {
|
|
2768
|
+
const cwd = process.cwd();
|
|
2769
|
+
if (!await pathExists(getSpecBridgeDir(cwd))) {
|
|
2770
|
+
throw new NotInitializedError();
|
|
2771
|
+
}
|
|
2772
|
+
const spinner = ora5("Detecting hook system...").start();
|
|
2773
|
+
try {
|
|
2774
|
+
let hookPath;
|
|
2775
|
+
let hookContent;
|
|
2776
|
+
if (options.husky) {
|
|
2783
2777
|
hookPath = join7(cwd, ".husky", "pre-commit");
|
|
2784
2778
|
hookContent = HOOK_SCRIPT;
|
|
2785
2779
|
spinner.text = "Installing husky pre-commit hook...";
|
|
2786
|
-
} else if (
|
|
2780
|
+
} else if (options.lefthook) {
|
|
2787
2781
|
spinner.succeed("Lefthook detected");
|
|
2788
2782
|
console.log("");
|
|
2789
2783
|
console.log(chalk8.cyan("Add this to your lefthook.yml:"));
|
|
@@ -2796,97 +2790,117 @@ hookCommand.command("install").description("Install Git pre-commit hook").option
|
|
|
2796
2790
|
`));
|
|
2797
2791
|
return;
|
|
2798
2792
|
} else {
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2793
|
+
if (await pathExists(join7(cwd, ".husky"))) {
|
|
2794
|
+
hookPath = join7(cwd, ".husky", "pre-commit");
|
|
2795
|
+
hookContent = HOOK_SCRIPT;
|
|
2796
|
+
spinner.text = "Installing husky pre-commit hook...";
|
|
2797
|
+
} else if (await pathExists(join7(cwd, "lefthook.yml"))) {
|
|
2798
|
+
spinner.succeed("Lefthook detected");
|
|
2799
|
+
console.log("");
|
|
2800
|
+
console.log(chalk8.cyan("Add this to your lefthook.yml:"));
|
|
2801
|
+
console.log("");
|
|
2802
|
+
console.log(chalk8.dim(`pre-commit:
|
|
2803
|
+
commands:
|
|
2804
|
+
specbridge:
|
|
2805
|
+
glob: "*.{ts,tsx}"
|
|
2806
|
+
run: npx specbridge hook run --level commit --files {staged_files}
|
|
2807
|
+
`));
|
|
2808
|
+
return;
|
|
2809
|
+
} else {
|
|
2810
|
+
hookPath = join7(cwd, ".git", "hooks", "pre-commit");
|
|
2811
|
+
hookContent = HOOK_SCRIPT;
|
|
2812
|
+
spinner.text = "Installing Git pre-commit hook...";
|
|
2813
|
+
}
|
|
2802
2814
|
}
|
|
2815
|
+
if (await pathExists(hookPath) && !options.force) {
|
|
2816
|
+
spinner.fail("Hook already exists");
|
|
2817
|
+
console.log(chalk8.yellow(`Use --force to overwrite: ${hookPath}`));
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
await writeTextFile(hookPath, hookContent);
|
|
2821
|
+
const { execSync } = await import("child_process");
|
|
2822
|
+
try {
|
|
2823
|
+
execSync(`chmod +x "${hookPath}"`, { stdio: "ignore" });
|
|
2824
|
+
} catch {
|
|
2825
|
+
}
|
|
2826
|
+
spinner.succeed("Pre-commit hook installed");
|
|
2827
|
+
console.log(chalk8.dim(` Path: ${hookPath}`));
|
|
2828
|
+
console.log("");
|
|
2829
|
+
console.log(chalk8.cyan("The hook will run on each commit and verify staged files."));
|
|
2830
|
+
} catch (error) {
|
|
2831
|
+
spinner.fail("Failed to install hook");
|
|
2832
|
+
throw error;
|
|
2803
2833
|
}
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2834
|
+
});
|
|
2835
|
+
hookCommand2.command("run").description("Run verification (called by hook)").option("-l, --level <level>", "Verification level", "commit").option("-f, --files <files>", "Space or comma-separated file list").action(async (options) => {
|
|
2836
|
+
const cwd = process.cwd();
|
|
2837
|
+
if (!await pathExists(getSpecBridgeDir(cwd))) {
|
|
2838
|
+
throw new NotInitializedError();
|
|
2808
2839
|
}
|
|
2809
|
-
await writeTextFile(hookPath, hookContent);
|
|
2810
|
-
const { execSync } = await import("child_process");
|
|
2811
2840
|
try {
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
});
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
if (result.violations.length === 0) {
|
|
2843
|
-
console.log(chalk8.green("\u2713 SpecBridge: All checks passed"));
|
|
2844
|
-
process.exit(0);
|
|
2845
|
-
}
|
|
2846
|
-
console.log(chalk8.red(`\u2717 SpecBridge: ${result.violations.length} violation(s) found`));
|
|
2847
|
-
console.log("");
|
|
2848
|
-
for (const v of result.violations) {
|
|
2849
|
-
const location = v.line ? `:${v.line}` : "";
|
|
2850
|
-
console.log(` ${v.file}${location}: ${v.message}`);
|
|
2851
|
-
console.log(chalk8.dim(` [${v.severity}] ${v.decisionId}/${v.constraintId}`));
|
|
2841
|
+
const config = await loadConfig(cwd);
|
|
2842
|
+
const level = options.level || "commit";
|
|
2843
|
+
const files = options.files ? options.files.split(/[\s,]+/).filter((f) => f.length > 0) : void 0;
|
|
2844
|
+
if (!files || files.length === 0) {
|
|
2845
|
+
process.exit(0);
|
|
2846
|
+
}
|
|
2847
|
+
const engine = createVerificationEngine();
|
|
2848
|
+
const result = await engine.verify(config, {
|
|
2849
|
+
level,
|
|
2850
|
+
files,
|
|
2851
|
+
cwd
|
|
2852
|
+
});
|
|
2853
|
+
if (result.violations.length === 0) {
|
|
2854
|
+
console.log(chalk8.green("\u2713 SpecBridge: All checks passed"));
|
|
2855
|
+
process.exit(0);
|
|
2856
|
+
}
|
|
2857
|
+
console.log(chalk8.red(`\u2717 SpecBridge: ${result.violations.length} violation(s) found`));
|
|
2858
|
+
console.log("");
|
|
2859
|
+
for (const v of result.violations) {
|
|
2860
|
+
const location = v.line ? `:${v.line}` : "";
|
|
2861
|
+
console.log(` ${v.file}${location}: ${v.message}`);
|
|
2862
|
+
console.log(chalk8.dim(` [${v.severity}] ${v.decisionId}/${v.constraintId}`));
|
|
2863
|
+
}
|
|
2864
|
+
console.log("");
|
|
2865
|
+
console.log(chalk8.yellow("Run `specbridge verify` for full details."));
|
|
2866
|
+
process.exit(result.success ? 0 : 1);
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
console.error(chalk8.red("SpecBridge verification failed"));
|
|
2869
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
2870
|
+
process.exit(1);
|
|
2852
2871
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
process.
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
if (await pathExists(hookPath)) {
|
|
2873
|
-
const content = await readTextFile(hookPath);
|
|
2874
|
-
if (content.includes("SpecBridge")) {
|
|
2875
|
-
const { unlink } = await import("fs/promises");
|
|
2876
|
-
await unlink(hookPath);
|
|
2877
|
-
spinner.succeed(`Removed hook: ${hookPath}`);
|
|
2878
|
-
removed = true;
|
|
2872
|
+
});
|
|
2873
|
+
hookCommand2.command("uninstall").description("Remove Git pre-commit hook").action(async () => {
|
|
2874
|
+
const cwd = process.cwd();
|
|
2875
|
+
const spinner = ora5("Removing hook...").start();
|
|
2876
|
+
try {
|
|
2877
|
+
const hookPaths = [
|
|
2878
|
+
join7(cwd, ".husky", "pre-commit"),
|
|
2879
|
+
join7(cwd, ".git", "hooks", "pre-commit")
|
|
2880
|
+
];
|
|
2881
|
+
let removed = false;
|
|
2882
|
+
for (const hookPath of hookPaths) {
|
|
2883
|
+
if (await pathExists(hookPath)) {
|
|
2884
|
+
const content = await readTextFile(hookPath);
|
|
2885
|
+
if (content.includes("SpecBridge")) {
|
|
2886
|
+
const { unlink } = await import("fs/promises");
|
|
2887
|
+
await unlink(hookPath);
|
|
2888
|
+
spinner.succeed(`Removed hook: ${hookPath}`);
|
|
2889
|
+
removed = true;
|
|
2890
|
+
}
|
|
2879
2891
|
}
|
|
2880
2892
|
}
|
|
2893
|
+
if (!removed) {
|
|
2894
|
+
spinner.info("No SpecBridge hooks found");
|
|
2895
|
+
}
|
|
2896
|
+
} catch (error) {
|
|
2897
|
+
spinner.fail("Failed to remove hook");
|
|
2898
|
+
throw error;
|
|
2881
2899
|
}
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
spinner.fail("Failed to remove hook");
|
|
2887
|
-
throw error;
|
|
2888
|
-
}
|
|
2889
|
-
});
|
|
2900
|
+
});
|
|
2901
|
+
return hookCommand2;
|
|
2902
|
+
}
|
|
2903
|
+
var hookCommand = createHookCommand();
|
|
2890
2904
|
|
|
2891
2905
|
// src/cli/commands/report.ts
|
|
2892
2906
|
import { Command as Command10 } from "commander";
|
|
@@ -3101,7 +3115,7 @@ function formatMarkdownReport(report) {
|
|
|
3101
3115
|
lines.push("| Decision | Status | Constraints | Violations | Compliance |");
|
|
3102
3116
|
lines.push("|----------|--------|-------------|------------|------------|");
|
|
3103
3117
|
for (const dec of report.byDecision) {
|
|
3104
|
-
const complianceEmoji = dec.compliance >= 90 ? "" : dec.compliance >= 70 ? "" : "";
|
|
3118
|
+
const complianceEmoji = dec.compliance >= 90 ? "\u2705" : dec.compliance >= 70 ? "\u26A0\uFE0F" : "\u274C";
|
|
3105
3119
|
lines.push(
|
|
3106
3120
|
`| ${dec.title} | ${dec.status} | ${dec.constraints} | ${dec.violations} | ${complianceEmoji} ${dec.compliance}% |`
|
|
3107
3121
|
);
|
|
@@ -3110,15 +3124,15 @@ function formatMarkdownReport(report) {
|
|
|
3110
3124
|
}
|
|
3111
3125
|
lines.push("---");
|
|
3112
3126
|
lines.push("");
|
|
3113
|
-
lines.push("*Generated by [SpecBridge](https://github.com/
|
|
3127
|
+
lines.push("*Generated by [SpecBridge](https://github.com/nouatzi/specbridge)*");
|
|
3114
3128
|
return lines.join("\n");
|
|
3115
3129
|
}
|
|
3116
3130
|
function formatProgressBar(percentage) {
|
|
3117
3131
|
const width = 20;
|
|
3118
3132
|
const filled = Math.round(percentage / 100 * width);
|
|
3119
3133
|
const empty = width - filled;
|
|
3120
|
-
const filledChar = "";
|
|
3121
|
-
const emptyChar = "";
|
|
3134
|
+
const filledChar = "\u2588";
|
|
3135
|
+
const emptyChar = "\u2591";
|
|
3122
3136
|
return `\`${filledChar.repeat(filled)}${emptyChar.repeat(empty)}\` ${percentage}%`;
|
|
3123
3137
|
}
|
|
3124
3138
|
|
|
@@ -3236,7 +3250,7 @@ function formatContextAsMarkdown(context) {
|
|
|
3236
3250
|
lines.push("### Constraints");
|
|
3237
3251
|
lines.push("");
|
|
3238
3252
|
for (const constraint of decision.constraints) {
|
|
3239
|
-
const typeEmoji = constraint.type === "invariant" ? "" : constraint.type === "convention" ? "" : "";
|
|
3253
|
+
const typeEmoji = constraint.type === "invariant" ? "\u{1F512}" : constraint.type === "convention" ? "\u{1F4CB}" : "\u{1F4A1}";
|
|
3240
3254
|
const severityBadge = `[${constraint.severity.toUpperCase()}]`;
|
|
3241
3255
|
lines.push(`- ${typeEmoji} **${severityBadge}** ${constraint.rule}`);
|
|
3242
3256
|
}
|
|
@@ -3293,7 +3307,7 @@ var contextCommand = new Command11("context").description("Generate architectura
|
|
|
3293
3307
|
const config = await loadConfig(cwd);
|
|
3294
3308
|
const output = await generateFormattedContext(file, config, {
|
|
3295
3309
|
format: options.format,
|
|
3296
|
-
includeRationale: options.
|
|
3310
|
+
includeRationale: options.rationale !== false,
|
|
3297
3311
|
cwd
|
|
3298
3312
|
});
|
|
3299
3313
|
if (options.output) {
|