bmad-visio 0.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/LICENSE +23 -0
- package/README.md +69 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.js +302 -0
- package/dist/git/index.js.map +1 -0
- package/dist/parser/index.d.ts +20 -0
- package/dist/parser/index.js +608 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +310 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types/index.d.ts +43 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
William Mesiti
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# bmad-visio
|
|
2
|
+
|
|
3
|
+
Visualize [BMAD Method](https://github.com/bmad-method) epics and user stories from structured `.md` files on a localhost dashboard. Optionally uses local AI (Transformers.js) to match git commits to stories.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx bmad-visio
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Run from your project root. Auto-detects BMAD structure:
|
|
12
|
+
|
|
13
|
+
- `_bmad-output/planning-artifacts/` + `_bmad-output/implementation-artifacts/` (new BMAD)
|
|
14
|
+
- `docs/sprint-artifacts/epics/` + `docs/sprint-artifacts/stories/` (old BMAD)
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Epic overview** — Grid of all epics with story counts and progress
|
|
19
|
+
- **Kanban board** — Stories in 4 columns: To Do → Active → Review → Done
|
|
20
|
+
- **Drag & drop** — Move stories between columns, writes status back to `.md` files
|
|
21
|
+
- **Story detail** — Acceptance criteria, tasks with checkboxes, progress ring
|
|
22
|
+
- **Live editing** — Toggle AC and tasks directly, updates files on disk
|
|
23
|
+
- **Git commit matching** — AI-powered mapping of commits to stories (optional)
|
|
24
|
+
|
|
25
|
+
## AI Commit Matching
|
|
26
|
+
|
|
27
|
+
Install the optional dependency for commit-to-story matching:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @huggingface/transformers
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The dashboard will then:
|
|
34
|
+
|
|
35
|
+
1. Parse your git history
|
|
36
|
+
2. Embed stories and commits with `all-MiniLM-L6-v2`
|
|
37
|
+
3. Re-rank ambiguous matches with zero-shot NLI (`mobilebert-uncased-mnli`)
|
|
38
|
+
4. Persist mappings in `bmad-visio/gitmap.json`
|
|
39
|
+
5. Show related commits on each story's detail page
|
|
40
|
+
|
|
41
|
+
First run downloads models (~180MB total). Everything runs locally, no API keys needed.
|
|
42
|
+
|
|
43
|
+
## Options
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
npx bmad-visio [path] [options]
|
|
47
|
+
|
|
48
|
+
Arguments:
|
|
49
|
+
path Project root (default: .)
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
-p, --port <n> Port number (default: 3333)
|
|
53
|
+
--no-git Skip git commit matching
|
|
54
|
+
--debug Dump parsed data as JSON and exit
|
|
55
|
+
-h, --help Show help
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Status Mapping
|
|
59
|
+
|
|
60
|
+
| BMAD Status | Board Column |
|
|
61
|
+
| -------------------------- | ---------------- |
|
|
62
|
+
| `backlog`, `ready-for-dev` | To Do |
|
|
63
|
+
| `in-progress` | Active |
|
|
64
|
+
| `review` | Ready for Review |
|
|
65
|
+
| `done` | Done |
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseBmadProject } from "./parser/index.js";
|
|
4
|
+
import { matchCommitsToStories } from "./git/index.js";
|
|
5
|
+
import { createServer } from "./server/index.js";
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
8
|
+
console.log(`
|
|
9
|
+
bmad-visio — Visualize BMAD epics, stories & git history
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
npx bmad-visio [path] [options]
|
|
13
|
+
|
|
14
|
+
Arguments:
|
|
15
|
+
path Project root (default: .)
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
-p, --port <n> Port number (default: 3333)
|
|
19
|
+
--no-git Skip git commit matching
|
|
20
|
+
--debug Dump parsed data as JSON and exit
|
|
21
|
+
-h, --help Show this help
|
|
22
|
+
|
|
23
|
+
Detects BMAD structure automatically:
|
|
24
|
+
_bmad-output/planning-artifacts/ (new BMAD)
|
|
25
|
+
docs/sprint-artifacts/ (old BMAD)
|
|
26
|
+
`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
const portIdx = args.findIndex((a) => a === "-p" || a === "--port");
|
|
30
|
+
const port = portIdx !== -1 ? parseInt(args[portIdx + 1], 10) : 3333;
|
|
31
|
+
const debug = args.includes("--debug");
|
|
32
|
+
const noGit = args.includes("--no-git");
|
|
33
|
+
const dir = path.resolve(args.find((a) => !a.startsWith("-") && a !== String(port)) ?? ".");
|
|
34
|
+
console.log(`\n 📂 Scanning: ${dir}`);
|
|
35
|
+
const project = parseBmadProject({ dir });
|
|
36
|
+
const storyCount = project.epics.reduce((n, e) => n + e.stories.length, 0);
|
|
37
|
+
console.log(` ✅ Found ${project.epics.length} epics, ${storyCount} stories`);
|
|
38
|
+
if (debug) {
|
|
39
|
+
console.log(JSON.stringify(project.epics, null, 2));
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
// Git matching
|
|
43
|
+
if (!noGit) {
|
|
44
|
+
try {
|
|
45
|
+
const mappings = await matchCommitsToStories(project, dir);
|
|
46
|
+
project.commitMappings = mappings;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.log(` ⚠️ Git matching skipped: ${err.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.log(" ⏭️ Git matching skipped (--no-git)");
|
|
54
|
+
}
|
|
55
|
+
createServer(project, port);
|
|
56
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;GAkBX,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC;AACpE,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACrE,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACvC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAExC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CACtB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAClE,CAAC;AAEF,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;AAExC,MAAM,OAAO,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;AAE1C,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC3E,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,KAAK,CAAC,MAAM,WAAW,UAAU,UAAU,CAAC,CAAC;AAE/E,IAAI,KAAK,EAAE,CAAC;IACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,eAAe;AACf,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC3D,OAAO,CAAC,cAAc,GAAG,QAAQ,CAAC;IACpC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,+BAA+B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;AACvD,CAAC;AAED,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { simpleGit } from "simple-git";
|
|
4
|
+
// ─── Math helpers ───────────────────────────────────────────────────
|
|
5
|
+
function dotProduct(a, b) {
|
|
6
|
+
let sum = 0;
|
|
7
|
+
for (let i = 0; i < a.length; i++)
|
|
8
|
+
sum += a[i] * b[i];
|
|
9
|
+
return sum;
|
|
10
|
+
}
|
|
11
|
+
// ─── Keyword overlap (Jaccard on lowercased tokens) ─────────────────
|
|
12
|
+
function tokenize(text) {
|
|
13
|
+
return new Set(text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 2));
|
|
14
|
+
}
|
|
15
|
+
function jaccardSimilarity(a, b) {
|
|
16
|
+
if (a.size === 0 && b.size === 0)
|
|
17
|
+
return 0;
|
|
18
|
+
let intersection = 0;
|
|
19
|
+
for (const t of a) {
|
|
20
|
+
if (b.has(t))
|
|
21
|
+
intersection++;
|
|
22
|
+
}
|
|
23
|
+
return intersection / (a.size + b.size - intersection);
|
|
24
|
+
}
|
|
25
|
+
// ─── Build text for embedding / matching ────────────────────────────
|
|
26
|
+
function storyEmbedText(story) {
|
|
27
|
+
const parts = [`Story ${story.id}: ${story.title}`];
|
|
28
|
+
if (story.description)
|
|
29
|
+
parts.push(story.description);
|
|
30
|
+
if (story.acceptanceCriteria.length > 0) {
|
|
31
|
+
parts.push(story.acceptanceCriteria.map((ac) => ac.description).join(". "));
|
|
32
|
+
}
|
|
33
|
+
return parts.join(" — ").slice(0, 500);
|
|
34
|
+
}
|
|
35
|
+
/** Short label for zero-shot NLI — just title + story text */
|
|
36
|
+
function storyLabel(story) {
|
|
37
|
+
const parts = [story.title];
|
|
38
|
+
if (story.description)
|
|
39
|
+
parts.push(story.description.split("\n")[0]);
|
|
40
|
+
return parts.join(": ").slice(0, 150);
|
|
41
|
+
}
|
|
42
|
+
function commitEmbedText(message, body, files) {
|
|
43
|
+
const parts = [message];
|
|
44
|
+
if (body)
|
|
45
|
+
parts.push(body);
|
|
46
|
+
const filePart = files.slice(0, 10).join(", ");
|
|
47
|
+
if (filePart)
|
|
48
|
+
parts.push(`[files: ${filePart}]`);
|
|
49
|
+
return parts.join(" ").slice(0, 500);
|
|
50
|
+
}
|
|
51
|
+
function commitNliText(message, body) {
|
|
52
|
+
const parts = [message];
|
|
53
|
+
if (body)
|
|
54
|
+
parts.push(body);
|
|
55
|
+
return parts.join(". ").slice(0, 200);
|
|
56
|
+
}
|
|
57
|
+
// ─── Gitmap file I/O ────────────────────────────────────────────────
|
|
58
|
+
function getGitMapPath(projectDir) {
|
|
59
|
+
return path.join(projectDir, "bmad-visio", "gitmap.json");
|
|
60
|
+
}
|
|
61
|
+
function loadGitMap(projectDir) {
|
|
62
|
+
const p = getGitMapPath(projectDir);
|
|
63
|
+
if (fs.existsSync(p)) {
|
|
64
|
+
try {
|
|
65
|
+
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
66
|
+
if (data.version === 1)
|
|
67
|
+
return { version: 2, mappings: data.mappings ?? {} };
|
|
68
|
+
return data;
|
|
69
|
+
}
|
|
70
|
+
catch { /* corrupted */ }
|
|
71
|
+
}
|
|
72
|
+
return { version: 2, mappings: {} };
|
|
73
|
+
}
|
|
74
|
+
function saveGitMap(projectDir, gitmap) {
|
|
75
|
+
const p = getGitMapPath(projectDir);
|
|
76
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
77
|
+
fs.writeFileSync(p, JSON.stringify({ version: 2, mappings: gitmap.mappings }, null, 2), "utf-8");
|
|
78
|
+
}
|
|
79
|
+
// ─── Model loaders (lazy, cached) ───────────────────────────────────
|
|
80
|
+
let _embedFn = null;
|
|
81
|
+
let _classifyFn = null;
|
|
82
|
+
let _transformersAvailable = null;
|
|
83
|
+
async function checkTransformers() {
|
|
84
|
+
if (_transformersAvailable !== null)
|
|
85
|
+
return _transformersAvailable;
|
|
86
|
+
try {
|
|
87
|
+
await import("@huggingface/transformers");
|
|
88
|
+
_transformersAvailable = true;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
_transformersAvailable = false;
|
|
92
|
+
}
|
|
93
|
+
return _transformersAvailable;
|
|
94
|
+
}
|
|
95
|
+
async function getEmbedFn() {
|
|
96
|
+
if (_embedFn)
|
|
97
|
+
return _embedFn;
|
|
98
|
+
console.log(" 🤖 Loading embedding model...");
|
|
99
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
100
|
+
const extractor = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
|
|
101
|
+
console.log(" ✅ Embedding model loaded");
|
|
102
|
+
_embedFn = async (texts) => {
|
|
103
|
+
const results = [];
|
|
104
|
+
for (let i = 0; i < texts.length; i += 16) {
|
|
105
|
+
const batch = texts.slice(i, i + 16);
|
|
106
|
+
const output = await extractor(batch, { pooling: "mean", normalize: true });
|
|
107
|
+
results.push(...output.tolist());
|
|
108
|
+
}
|
|
109
|
+
return results;
|
|
110
|
+
};
|
|
111
|
+
return _embedFn;
|
|
112
|
+
}
|
|
113
|
+
async function getClassifyFn() {
|
|
114
|
+
if (_classifyFn)
|
|
115
|
+
return _classifyFn;
|
|
116
|
+
console.log(" 🧠 Loading zero-shot classifier for re-ranking...");
|
|
117
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
118
|
+
const classifier = await pipeline("zero-shot-classification", "Xenova/mobilebert-uncased-mnli");
|
|
119
|
+
console.log(" ✅ Classifier loaded");
|
|
120
|
+
_classifyFn = async (text, labels) => {
|
|
121
|
+
const result = await classifier(text, labels, { multi_label: true });
|
|
122
|
+
// Result shape: { sequence, labels: string[], scores: number[] }
|
|
123
|
+
return result.labels.map((label, i) => ({
|
|
124
|
+
label,
|
|
125
|
+
score: result.scores[i],
|
|
126
|
+
}));
|
|
127
|
+
};
|
|
128
|
+
return _classifyFn;
|
|
129
|
+
}
|
|
130
|
+
// ─── Scoring weights ────────────────────────────────────────────────
|
|
131
|
+
//
|
|
132
|
+
// Three-stage pipeline for the report:
|
|
133
|
+
// Stage 1 — Bi-encoder embedding: fast retrieval, narrows to top K
|
|
134
|
+
// Stage 2 — Heuristics: story ID regex + keyword overlap
|
|
135
|
+
// Stage 3 — Zero-shot NLI: re-ranks ambiguous candidates
|
|
136
|
+
//
|
|
137
|
+
// Final score = weighted combination of all signals.
|
|
138
|
+
const WEIGHTS = {
|
|
139
|
+
embedding: 0.35,
|
|
140
|
+
keyword: 0.15,
|
|
141
|
+
idBoost: 0.20, // only when an ID is detected
|
|
142
|
+
nli: 0.30,
|
|
143
|
+
};
|
|
144
|
+
const MATCH_THRESHOLD = 0.25; // on the combined scale
|
|
145
|
+
const RERANK_TOP_K = 3; // send top K to the NLI classifier
|
|
146
|
+
const RERANK_AMBIGUITY = 0.12; // trigger re-rank if gap between #1 and #2 is below this
|
|
147
|
+
const RERANK_CEILING = 0.55; // always re-rank if top score is below this
|
|
148
|
+
// ─── Main: match commits to stories ─────────────────────────────────
|
|
149
|
+
export async function matchCommitsToStories(project, projectDir, opts) {
|
|
150
|
+
const allStories = [];
|
|
151
|
+
for (const epic of project.epics)
|
|
152
|
+
allStories.push(...epic.stories);
|
|
153
|
+
if (allStories.length === 0)
|
|
154
|
+
return [];
|
|
155
|
+
// ── Git log ──
|
|
156
|
+
const git = simpleGit(projectDir);
|
|
157
|
+
let commits;
|
|
158
|
+
try {
|
|
159
|
+
const log = await git.log(["--name-only"]);
|
|
160
|
+
commits = log.all.map((entry) => ({
|
|
161
|
+
sha: entry.hash, message: entry.message,
|
|
162
|
+
body: entry.body?.trim() ?? "", date: entry.date,
|
|
163
|
+
files: (entry.diff?.files?.map((f) => f.file) ?? []),
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
console.log(" ⚠️ Not a git repository or git not available");
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
if (commits.length === 0)
|
|
171
|
+
return [];
|
|
172
|
+
// ── Load gitmap, find new commits ──
|
|
173
|
+
const gitmap = loadGitMap(projectDir);
|
|
174
|
+
const newCommits = commits.filter((c) => !gitmap.mappings[c.sha]);
|
|
175
|
+
if (newCommits.length === 0) {
|
|
176
|
+
console.log(" 📋 All commits already mapped");
|
|
177
|
+
return Object.values(gitmap.mappings);
|
|
178
|
+
}
|
|
179
|
+
if (opts?.skipEmbedding)
|
|
180
|
+
return Object.values(gitmap.mappings);
|
|
181
|
+
// Check if @huggingface/transformers is installed (it's an optional dep)
|
|
182
|
+
if (!(await checkTransformers())) {
|
|
183
|
+
console.log(" ⚠️ @huggingface/transformers not installed — run: npm install @huggingface/transformers");
|
|
184
|
+
console.log(" Skipping AI commit matching. Dashboard will work without it.");
|
|
185
|
+
return Object.values(gitmap.mappings);
|
|
186
|
+
}
|
|
187
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
188
|
+
// STAGE 1: Bi-encoder embedding — fast retrieval
|
|
189
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
190
|
+
const embed = await getEmbedFn();
|
|
191
|
+
const storyIds = allStories.map((s) => s.id);
|
|
192
|
+
const storyIdSet = new Set(storyIds);
|
|
193
|
+
const storyTexts = allStories.map((s) => storyEmbedText(s));
|
|
194
|
+
const storyLabels = allStories.map((s) => storyLabel(s));
|
|
195
|
+
const storyTokens = storyTexts.map((t) => tokenize(t));
|
|
196
|
+
console.log(` 📝 Embedding ${storyTexts.length} stories...`);
|
|
197
|
+
const storyVecs = await embed(storyTexts);
|
|
198
|
+
console.log(` 🔗 Matching ${newCommits.length} new commits...`);
|
|
199
|
+
const commitTexts = newCommits.map((c) => commitEmbedText(c.message, c.body, c.files));
|
|
200
|
+
const commitVectors = await embed(commitTexts);
|
|
201
|
+
const allCandidates = [];
|
|
202
|
+
for (let ci = 0; ci < newCommits.length; ci++) {
|
|
203
|
+
const commit = newCommits[ci];
|
|
204
|
+
const cVec = commitVectors[ci];
|
|
205
|
+
const fullText = `${commit.message} ${commit.body}`;
|
|
206
|
+
const commitTokens = tokenize(fullText);
|
|
207
|
+
// Detect story ID references
|
|
208
|
+
const referencedIds = new Set();
|
|
209
|
+
const idPattern = /\b(\d+\.\d+)\b/g;
|
|
210
|
+
let m;
|
|
211
|
+
while ((m = idPattern.exec(fullText)) !== null) {
|
|
212
|
+
if (storyIdSet.has(m[1]))
|
|
213
|
+
referencedIds.add(m[1]);
|
|
214
|
+
}
|
|
215
|
+
const candidates = storyIds.map((sid, si) => ({
|
|
216
|
+
storyId: sid,
|
|
217
|
+
embeddingScore: dotProduct(cVec, storyVecs[si]),
|
|
218
|
+
idBoost: referencedIds.has(sid) ? 1.0 : 0.0,
|
|
219
|
+
keywordScore: jaccardSimilarity(commitTokens, storyTokens[si]),
|
|
220
|
+
nliScore: 0, // filled in stage 3
|
|
221
|
+
finalScore: 0, // computed after all stages
|
|
222
|
+
}));
|
|
223
|
+
// Pre-compute score without NLI to determine if re-rank is needed
|
|
224
|
+
for (const c of candidates) {
|
|
225
|
+
c.finalScore = c.embeddingScore * (WEIGHTS.embedding + WEIGHTS.nli) // NLI weight goes to embedding when skipped
|
|
226
|
+
+ c.keywordScore * WEIGHTS.keyword
|
|
227
|
+
+ c.idBoost * WEIGHTS.idBoost;
|
|
228
|
+
}
|
|
229
|
+
candidates.sort((a, b) => b.finalScore - a.finalScore);
|
|
230
|
+
// Decide if we need NLI re-ranking
|
|
231
|
+
const top = candidates[0];
|
|
232
|
+
const second = candidates[1];
|
|
233
|
+
const gap = top && second ? top.finalScore - second.finalScore : 1;
|
|
234
|
+
const needsRerank = top.finalScore < RERANK_CEILING || gap < RERANK_AMBIGUITY;
|
|
235
|
+
allCandidates.push({ commit, candidates, needsRerank });
|
|
236
|
+
}
|
|
237
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
238
|
+
// STAGE 3: Zero-shot NLI re-ranking (only for ambiguous matches)
|
|
239
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
240
|
+
const ambiguous = allCandidates.filter((cc) => cc.needsRerank);
|
|
241
|
+
if (ambiguous.length > 0) {
|
|
242
|
+
console.log(` 🧠 Re-ranking ${ambiguous.length}/${newCommits.length} ambiguous commits with NLI...`);
|
|
243
|
+
const classify = await getClassifyFn();
|
|
244
|
+
for (const cc of ambiguous) {
|
|
245
|
+
const topK = cc.candidates.slice(0, RERANK_TOP_K);
|
|
246
|
+
const commitText = commitNliText(cc.commit.message, cc.commit.body);
|
|
247
|
+
const labels = topK.map((c) => {
|
|
248
|
+
const idx = storyIds.indexOf(c.storyId);
|
|
249
|
+
return storyLabels[idx];
|
|
250
|
+
});
|
|
251
|
+
try {
|
|
252
|
+
const nliResults = await classify(commitText, labels);
|
|
253
|
+
// Map NLI scores back to candidates
|
|
254
|
+
for (const c of topK) {
|
|
255
|
+
const idx = storyIds.indexOf(c.storyId);
|
|
256
|
+
const label = storyLabels[idx];
|
|
257
|
+
const nliResult = nliResults.find((r) => r.label === label);
|
|
258
|
+
c.nliScore = nliResult?.score ?? 0;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
console.log(` ⚠️ NLI failed for "${cc.commit.message.slice(0, 40)}": ${err.message}`);
|
|
263
|
+
// Fall back — NLI scores stay at 0, embedding carries full weight
|
|
264
|
+
}
|
|
265
|
+
// Recompute final scores with NLI
|
|
266
|
+
for (const c of cc.candidates) {
|
|
267
|
+
c.finalScore =
|
|
268
|
+
c.embeddingScore * WEIGHTS.embedding +
|
|
269
|
+
c.keywordScore * WEIGHTS.keyword +
|
|
270
|
+
c.idBoost * WEIGHTS.idBoost +
|
|
271
|
+
c.nliScore * WEIGHTS.nli;
|
|
272
|
+
}
|
|
273
|
+
cc.candidates.sort((a, b) => b.finalScore - a.finalScore);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
console.log(` ⏭️ All matches confident, NLI re-ranking skipped`);
|
|
278
|
+
}
|
|
279
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
280
|
+
// Build final mappings
|
|
281
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
282
|
+
for (const cc of allCandidates) {
|
|
283
|
+
const best = cc.candidates[0];
|
|
284
|
+
const score = Math.min(Math.round(best.finalScore * 100) / 100, 1.0);
|
|
285
|
+
gitmap.mappings[cc.commit.sha] = {
|
|
286
|
+
sha: cc.commit.sha,
|
|
287
|
+
message: cc.commit.message,
|
|
288
|
+
body: cc.commit.body,
|
|
289
|
+
files: cc.commit.files,
|
|
290
|
+
date: cc.commit.date,
|
|
291
|
+
storyId: score >= MATCH_THRESHOLD ? best.storyId : null,
|
|
292
|
+
score,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
saveGitMap(projectDir, gitmap);
|
|
296
|
+
const allMappings = Object.values(gitmap.mappings);
|
|
297
|
+
const matched = allMappings.filter((m) => m.storyId !== null).length;
|
|
298
|
+
const reranked = ambiguous.length;
|
|
299
|
+
console.log(` ✅ ${matched}/${allMappings.length} commits matched (${reranked} re-ranked by NLI)`);
|
|
300
|
+
return allMappings;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BmadProject } from "../types/index.js";
|
|
2
|
+
export declare const BOARD_COLUMNS: readonly ["todo", "active", "review", "done"];
|
|
3
|
+
export type BoardStatus = (typeof BOARD_COLUMNS)[number];
|
|
4
|
+
export interface ParseOptions {
|
|
5
|
+
dir: string;
|
|
6
|
+
mode?: "auto" | "bmad-new" | "bmad-old" | "flat";
|
|
7
|
+
}
|
|
8
|
+
export declare function parseBmadProject(opts: ParseOptions): BmadProject;
|
|
9
|
+
export declare function updateStoryStatus(project: BmadProject, epicId: string, storyId: string, newStatus: BoardStatus): {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function toggleAcceptanceCriteria(project: BmadProject, epicId: string, storyId: string, acIndex: number, done: boolean): {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function toggleTask(project: BmadProject, epicId: string, storyId: string, taskId: string, done: boolean): {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
};
|