@sandrinio/vbounce 1.6.0 → 1.7.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.
Files changed (46) hide show
  1. package/README.md +108 -18
  2. package/bin/vbounce.mjs +291 -146
  3. package/brains/AGENTS.md +5 -5
  4. package/brains/CHANGELOG.md +88 -1
  5. package/brains/CLAUDE.md +22 -17
  6. package/brains/GEMINI.md +40 -4
  7. package/brains/SETUP.md +11 -5
  8. package/brains/claude-agents/architect.md +13 -6
  9. package/brains/claude-agents/developer.md +2 -2
  10. package/brains/claude-agents/qa.md +16 -7
  11. package/brains/copilot/copilot-instructions.md +49 -0
  12. package/brains/cursor-rules/vbounce-process.mdc +2 -2
  13. package/brains/windsurf/.windsurfrules +30 -0
  14. package/package.json +2 -4
  15. package/scripts/close_sprint.mjs +94 -0
  16. package/scripts/complete_story.mjs +113 -0
  17. package/scripts/doctor.mjs +144 -0
  18. package/scripts/init_sprint.mjs +121 -0
  19. package/scripts/prep_arch_context.mjs +178 -0
  20. package/scripts/prep_qa_context.mjs +134 -0
  21. package/scripts/prep_sprint_context.mjs +118 -0
  22. package/scripts/prep_sprint_summary.mjs +154 -0
  23. package/scripts/sprint_trends.mjs +160 -0
  24. package/scripts/suggest_improvements.mjs +200 -0
  25. package/scripts/update_state.mjs +132 -0
  26. package/scripts/validate_bounce_readiness.mjs +125 -0
  27. package/scripts/validate_report.mjs +39 -2
  28. package/scripts/validate_sprint_plan.mjs +117 -0
  29. package/scripts/validate_state.mjs +99 -0
  30. package/skills/agent-team/SKILL.md +35 -17
  31. package/skills/agent-team/references/cleanup.md +42 -0
  32. package/skills/agent-team/references/delivery-sync.md +43 -0
  33. package/skills/agent-team/references/git-strategy.md +52 -0
  34. package/skills/agent-team/references/mid-sprint-triage.md +71 -0
  35. package/skills/agent-team/references/report-naming.md +34 -0
  36. package/skills/doc-manager/SKILL.md +5 -4
  37. package/skills/improve/SKILL.md +1 -1
  38. package/skills/lesson/SKILL.md +23 -0
  39. package/templates/delivery_plan.md +1 -1
  40. package/templates/hotfix.md +1 -1
  41. package/templates/sprint.md +65 -13
  42. package/templates/sprint_report.md +8 -1
  43. package/templates/story.md +1 -1
  44. package/scripts/pre_bounce_sync.sh +0 -37
  45. package/scripts/vbounce_ask.mjs +0 -98
  46. package/scripts/vbounce_index.mjs +0 -184
@@ -3,7 +3,7 @@ FOLLOW THIS EXACT STRUCTURE. Output sections in order 1-6.
3
3
 
4
4
  1. **YAML Frontmatter**: Sprint ID, Goal, Dates, Status, Delivery Ref, Delivery Plan Ref
5
5
  2. **§1 What Was Delivered**: User-facing summary — what's accessible/usable vs what's internal/backend
6
- 3. **§2 Story Results**: Table of all stories with final status and per-story metrics
6
+ 3. **§2 Story Results**: Table of all stories with final status and per-story metrics. §2.1 Change Requests logs any mid-sprint user interventions (bugs, spec clarifications, scope/approach changes)
7
7
  4. **§3 Execution Metrics**: AI performance metrics — tokens, duration, bounces, correction tax
8
8
  5. **§4 Lessons Learned**: Flagged from agent reports, pending user approval to record
9
9
  6. **§5 Retrospective**: What went well, what didn't, and what to change — covers both project and delivery process
@@ -72,6 +72,13 @@ delivery_plan_ref: "product_plans/{delivery}/DELIVERY_PLAN.md"
72
72
  ### Escalated Stories (if any)
73
73
  - **STORY-{ID}-{story_name}**: Escalated after {N} bounces. Root cause: {why}. Recommendation: {rewrite spec / descope / kill}.
74
74
 
75
+ ### 2.1 Change Requests
76
+ > Mid-sprint user interventions — bugs found, spec clarifications, scope or approach changes. Tracked separately from agent-driven bounces so metrics aren't skewed. Omit this section if no CRs occurred.
77
+
78
+ | Story | Category | Description | Impact |
79
+ |-------|----------|-------------|--------|
80
+ | STORY-{ID} | Bug / Spec Clarification / Scope Change / Approach Change | {One-line description} | {e.g., No bounce reset / Dev pass reset / +1 session} |
81
+
75
82
  ---
76
83
 
77
84
  ## 3. Execution Metrics
@@ -153,4 +153,4 @@ POST /api/resource
153
153
  - [ ] LESSONS.md consulted before implementation.
154
154
  - [ ] No violations of Roadmap ADRs.
155
155
  - [ ] Documentation (API/Tech Stack) updated.
156
- - [ ] **Framework Integrity**: If `brains/` or `skills/` were modified, log to `brains/CHANGELOG.md` and run `./scripts/pre_bounce_sync.sh`.
156
+ - [ ] **Framework Integrity**: If `brains/` or `skills/` were modified, log to `brains/CHANGELOG.md`.
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # pre_bounce_sync.sh
4
- #
5
- # Run this before kicking off the Implementation Loop (Bounce).
6
- # This prevents the "Stale Context" edge case by forcing the LanceDB
7
- # index to refresh with the latest rules from LESSONS.md and ROADMAP.md.
8
-
9
- echo "==========================================="
10
- echo " V-Bounce OS: Pre-Bounce Sync Started"
11
- echo "==========================================="
12
-
13
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
14
- ROOT_DIR="$(dirname "$SCRIPT_DIR")"
15
-
16
- cd "$ROOT_DIR" || exit 1
17
-
18
- # 1. Check for Node Modules
19
- if [ ! -d "node_modules" ]; then
20
- echo "Error: node_modules not found. Run 'npm install'."
21
- exit 1
22
- fi
23
-
24
- # 2. Rebuild the semantic search index
25
- echo "Syncing V-Bounce Knowledge Base (LanceDB)..."
26
- node ./scripts/vbounce_index.mjs --all
27
-
28
- if [ $? -ne 0 ]; then
29
- echo "Error: LanceDB index sync failed."
30
- exit 1
31
- fi
32
-
33
- echo "==========================================="
34
- echo " Pre-Bounce Sync Complete. RAG is fresh."
35
- echo " Ready for Team Lead delegation."
36
- echo "==========================================="
37
- exit 0
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { connect } from '@lancedb/lancedb';
6
- import { pipeline } from '@xenova/transformers';
7
- import { program } from 'commander';
8
-
9
- const LANCE_DIR = path.join(process.cwd(), '.bounce', '.lancedb');
10
-
11
- program
12
- .name('vbounce_ask')
13
- .description('Query V-Bounce OS LanceDB for relevant context')
14
- .argument('<query>', 'The semantic query to search for')
15
- .option('-f, --filter <field=value>', 'Filter by metadata (e.g., type=lesson)')
16
- .option('-l, --limit <number>', 'Number of results to return', 3)
17
- .parse(process.argv);
18
-
19
- const options = program.opts();
20
- const query = program.args[0];
21
-
22
- class LocalEmbeddingFunction {
23
- constructor() {
24
- this.modelName = 'Xenova/all-MiniLM-L6-v2';
25
- this.extractor = null;
26
- }
27
-
28
- async init() {
29
- if (!this.extractor) {
30
- this.extractor = await pipeline('feature-extraction', this.modelName, {
31
- quantized: true,
32
- });
33
- }
34
- }
35
-
36
- async embedQuery(text) {
37
- await this.init();
38
- const output = await this.extractor(text, { pooling: 'mean', normalize: true });
39
- return Array.from(output.data);
40
- }
41
- }
42
-
43
- async function main() {
44
- if (!query) {
45
- console.error("Error: Must provide a semantic query string.");
46
- process.exit(1);
47
- }
48
-
49
- if (!fs.existsSync(LANCE_DIR)) {
50
- console.error(`Error: LanceDB not found at ${LANCE_DIR}. Have you run vbounce_index yet?`);
51
- process.exit(1);
52
- }
53
-
54
- const db = await connect(LANCE_DIR);
55
- let table;
56
- try {
57
- table = await db.openTable('vbounce_context');
58
- } catch (e) {
59
- console.error("Error: Table 'vbounce_context' not found. Please index documents first.");
60
- process.exit(1);
61
- }
62
-
63
- const embedder = new LocalEmbeddingFunction();
64
- const queryVector = await embedder.embedQuery(query);
65
-
66
- let search = table.vectorSearch(queryVector).limit(parseInt(options.limit));
67
-
68
- if (options.filter) {
69
- const [field, value] = options.filter.split('=');
70
- // LanceDB JS uses SQL-like string criteria for filtering
71
- if (field === "type") {
72
- search = search.where(`\`type\` = '${value}'`);
73
- } else if (field === "section") {
74
- search = search.where(`\`section\` = '${value}'`);
75
- }
76
- }
77
-
78
- const results = await search.toArray();
79
-
80
- if (results.length === 0) {
81
- console.log("No relevant context found.");
82
- return;
83
- }
84
-
85
- console.log(`\n--- V-Bounce Semantic Retrieval ---`);
86
- console.log(`Query: "${query}"`);
87
- console.log(`Found ${results.length} relevant chunks.\n`);
88
-
89
- results.forEach((r, idx) => {
90
- console.log(`[Result ${idx + 1}] Source: ${r.file} (${r.type || 'unknown'} > ${r.section || 'General'})`);
91
- console.log(`Distance: ${r._distance ? r._distance.toFixed(4) : 'N/A'}`);
92
- console.log('-'.repeat(40));
93
- console.log(r.text.trim());
94
- console.log('\n');
95
- });
96
- }
97
-
98
- main().catch(console.error);
@@ -1,184 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { connect } from '@lancedb/lancedb';
6
- import { pipeline } from '@xenova/transformers';
7
- import { marked } from 'marked';
8
- import { program } from 'commander';
9
-
10
- const LANCE_DIR = path.join(process.cwd(), '.bounce', '.lancedb');
11
-
12
- program
13
- .name('vbounce_index')
14
- .description('Index V-Bounce OS documents into local LanceDB')
15
- .argument('[path]', 'File or directory path to index (or use --all)')
16
- .option('--all', 'Index all standard V-Bounce directories')
17
- .parse(process.argv);
18
-
19
- const options = program.opts();
20
- const targetPath = program.args[0];
21
-
22
- // Initialize local embedding model wrapper
23
- class LocalEmbeddingFunction {
24
- constructor() {
25
- this.modelName = 'Xenova/all-MiniLM-L6-v2';
26
- this.extractor = null;
27
- }
28
-
29
- async init() {
30
- if (!this.extractor) {
31
- console.log(`Loading embedding model (${this.modelName})...`);
32
- this.extractor = await pipeline('feature-extraction', this.modelName, {
33
- quantized: true,
34
- });
35
- }
36
- }
37
-
38
- async computeSourceEmbeddings(texts) {
39
- await this.init();
40
- const embeddings = [];
41
- for (const text of texts) {
42
- const output = await this.extractor(text, { pooling: 'mean', normalize: true });
43
- embeddings.push(Array.from(output.data));
44
- }
45
- return embeddings;
46
- }
47
- }
48
-
49
- // Function to chunk Markdown files semantically (simplified for MVP)
50
- function chunkMarkdown(content, metadata) {
51
- const tokens = marked.lexer(content);
52
- const chunks = [];
53
- let currentHeader = 'General';
54
- let buffer = '';
55
-
56
- for (const token of tokens) {
57
- if (token.type === 'heading') {
58
- if (buffer.trim()) {
59
- chunks.push({ text: buffer.trim(), section: currentHeader, ...metadata });
60
- }
61
- currentHeader = token.text;
62
- buffer = `${token.raw}\n`;
63
- } else {
64
- buffer += `${token.raw}\n`;
65
- }
66
- }
67
-
68
- if (buffer.trim()) {
69
- chunks.push({ text: buffer.trim(), section: currentHeader, ...metadata });
70
- }
71
-
72
- return chunks;
73
- }
74
-
75
- async function indexFile(filePath, embedder) {
76
- console.log(`Processing file: ${filePath}`);
77
- const content = fs.readFileSync(filePath, 'utf-8');
78
- const basename = path.basename(filePath);
79
-
80
- let type = 'unknown';
81
- if (filePath.includes('LESSONS.md')) type = 'lesson';
82
- else if (filePath.includes('ROADMAP.md')) type = 'adr';
83
- else if (filePath.includes('.bounce/reports')) type = 'report';
84
- else if (filePath.includes('product_plans')) type = 'plan';
85
- else if (filePath.includes('vdocs')) type = 'documentation';
86
-
87
- const metadata = { file: basename, type };
88
- const chunks = chunkMarkdown(content, metadata);
89
-
90
- if (chunks.length === 0) return [];
91
-
92
- console.log(` Extracted ${chunks.length} chunks. Generating embeddings...`);
93
-
94
- const textsToEmbed = chunks.map(c => `[${c.type} - ${c.file} - ${c.section}]\n${c.text}`);
95
- const vectors = await embedder.computeSourceEmbeddings(textsToEmbed);
96
-
97
- return chunks.map((chunk, i) => ({
98
- id: `${chunk.file}-${i}`,
99
- file: chunk.file,
100
- type: chunk.type,
101
- section: chunk.section,
102
- text: chunk.text,
103
- vector: vectors[i]
104
- }));
105
- }
106
-
107
- async function main() {
108
- if (!targetPath && !options.all) {
109
- console.error("Error: Must specify a path or use --all");
110
- process.exit(1);
111
- }
112
-
113
- const embedder = new LocalEmbeddingFunction();
114
-
115
- // Ensure table exists
116
- if (!fs.existsSync(LANCE_DIR)) {
117
- fs.mkdirSync(LANCE_DIR, { recursive: true });
118
- }
119
-
120
- const db = await connect(LANCE_DIR);
121
- let table;
122
-
123
- try {
124
- table = await db.openTable('vbounce_context');
125
- } catch (e) {
126
- // Table doesn't exist, will create dynamically on first insert
127
- console.log("Creating new vbounce_context table...");
128
- }
129
-
130
- const filesToIndex = [];
131
-
132
- function walkDir(dir) {
133
- if (!fs.existsSync(dir)) return;
134
- const files = fs.readdirSync(dir);
135
- for (const file of files) {
136
- const fullPath = path.join(dir, file);
137
- const stat = fs.statSync(fullPath);
138
- if (stat.isDirectory()) {
139
- walkDir(fullPath);
140
- } else if (fullPath.endsWith('.md')) {
141
- filesToIndex.push(fullPath);
142
- }
143
- }
144
- }
145
-
146
- if (options.all) {
147
- if (fs.existsSync('LESSONS.md')) filesToIndex.push('LESSONS.md');
148
- if (fs.existsSync('ROADMAP.md')) filesToIndex.push('ROADMAP.md');
149
- walkDir('product_plans');
150
- walkDir('vdocs');
151
- walkDir('.bounce/reports');
152
- } else if (targetPath) {
153
- const stat = fs.statSync(targetPath);
154
- if (stat.isFile()) {
155
- filesToIndex.push(targetPath);
156
- } else if (stat.isDirectory()) {
157
- walkDir(targetPath);
158
- }
159
- }
160
-
161
- if (filesToIndex.length === 0) {
162
- console.log("No files found to index.");
163
- process.exit(0);
164
- }
165
-
166
- let allRecords = [];
167
- for (const file of filesToIndex) {
168
- const records = await indexFile(file, embedder);
169
- allRecords = allRecords.concat(records);
170
- }
171
-
172
- if (allRecords.length > 0) {
173
- if (table) {
174
- console.log(`Adding ${allRecords.length} records to existing table...`);
175
- await table.add(allRecords);
176
- } else {
177
- console.log(`Creating table with ${allRecords.length} records...`);
178
- table = await db.createTable('vbounce_context', allRecords);
179
- }
180
- console.log(`Successfully indexed into LanceDB.`);
181
- }
182
- }
183
-
184
- main().catch(console.error);