vibepro 0.1.0-alpha.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 (89) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +9 -0
  3. package/README.ja.md +448 -0
  4. package/README.md +520 -0
  5. package/agent-instructions/codex/AGENTS.vibepro.md +45 -0
  6. package/bin/vibepro.js +9 -0
  7. package/docs/assets/vibepro-header.png +0 -0
  8. package/package.json +51 -0
  9. package/skills/vibepro-diagnosis-packages/SKILL.md +133 -0
  10. package/skills/vibepro-human-review/SKILL.md +73 -0
  11. package/skills/vibepro-story-refactor/SKILL.md +89 -0
  12. package/skills/vibepro-workflow/SKILL.md +139 -0
  13. package/src/agent-harness-map.js +230 -0
  14. package/src/agent-harness-scanner.js +337 -0
  15. package/src/agent-review.js +2180 -0
  16. package/src/api-boundary-scanner.js +452 -0
  17. package/src/architecture-profiler.js +423 -0
  18. package/src/authorization-scoring.js +149 -0
  19. package/src/brainbase-importer.js +534 -0
  20. package/src/change-risk-classifier.js +195 -0
  21. package/src/check-packs.js +605 -0
  22. package/src/checkpoint-manager.js +233 -0
  23. package/src/cli.js +2213 -0
  24. package/src/code-quality-scanner.js +310 -0
  25. package/src/codex-manager.js +143 -0
  26. package/src/component-style-scanner.js +336 -0
  27. package/src/coverage-report.js +99 -0
  28. package/src/database-access-scanner.js +163 -0
  29. package/src/decision-records.js +315 -0
  30. package/src/design-modernize.js +1435 -0
  31. package/src/design-system.js +1732 -0
  32. package/src/diagnostic-engine.js +1945 -0
  33. package/src/diagram-requirement-resolver.js +194 -0
  34. package/src/doctor.js +677 -0
  35. package/src/environment-graph.js +424 -0
  36. package/src/execution-state.js +849 -0
  37. package/src/explore-evidence.js +425 -0
  38. package/src/flow-design-scanner.js +896 -0
  39. package/src/flow-verifier.js +887 -0
  40. package/src/gesture-interaction-scanner.js +330 -0
  41. package/src/graph-context.js +263 -0
  42. package/src/graphify-adapter.js +189 -0
  43. package/src/html-report.js +1035 -0
  44. package/src/journey-map.js +1299 -0
  45. package/src/language.js +48 -0
  46. package/src/lazy-pattern-detector.js +182 -0
  47. package/src/local-dev-scanner.js +135 -0
  48. package/src/managed-worktree-gate.js +187 -0
  49. package/src/managed-worktree.js +766 -0
  50. package/src/merge-manager.js +501 -0
  51. package/src/network-contract-scanner.js +442 -0
  52. package/src/nocodb-story-sync.js +386 -0
  53. package/src/oss-readiness-scanner.js +417 -0
  54. package/src/performance-evidence.js +756 -0
  55. package/src/performance-measurer.js +591 -0
  56. package/src/pr-manager.js +8220 -0
  57. package/src/presets.js +682 -0
  58. package/src/public-discovery-scanner.js +519 -0
  59. package/src/refactoring-delta-reporter.js +367 -0
  60. package/src/refactoring-opportunity-generator.js +797 -0
  61. package/src/regression-risk-scanner.js +146 -0
  62. package/src/repo-status.js +266 -0
  63. package/src/report-fingerprint.js +188 -0
  64. package/src/report-pr-body-prompt-template.md +108 -0
  65. package/src/report-pr-body-schema.json +95 -0
  66. package/src/report-store.js +135 -0
  67. package/src/report-validator.js +192 -0
  68. package/src/requirement-consistency.js +1066 -0
  69. package/src/runtime-info.js +134 -0
  70. package/src/self-dogfood-scanner.js +476 -0
  71. package/src/session-learning.js +164 -0
  72. package/src/skills-manager.js +157 -0
  73. package/src/spec-drift.js +378 -0
  74. package/src/spec-fingerprint.js +445 -0
  75. package/src/spec-prompt-template.md +155 -0
  76. package/src/spec-schema.json +219 -0
  77. package/src/spec-store.js +258 -0
  78. package/src/spec-validator.js +459 -0
  79. package/src/static-site-scanner.js +316 -0
  80. package/src/story-candidate-generator.js +85 -0
  81. package/src/story-catalog-generator.js +2813 -0
  82. package/src/story-html.js +156 -0
  83. package/src/story-manager.js +2144 -0
  84. package/src/story-task-generator.js +522 -0
  85. package/src/task-manager.js +1029 -0
  86. package/src/terminal-link-scanner.js +238 -0
  87. package/src/usage-report.js +417 -0
  88. package/src/verification-evidence.js +284 -0
  89. package/src/workspace.js +126 -0
@@ -0,0 +1,367 @@
1
+ const SCHEMA_VERSION = '0.1.0';
2
+
3
+ export function buildRefactoringDelta({ beforeEvidence = null, afterEvidence = null, beforeRun = null } = {}) {
4
+ const afterRunId = afterEvidence?.run_id ?? null;
5
+ const beforeRunId = beforeEvidence?.run_id ?? beforeRun?.run_id ?? null;
6
+ if (!beforeEvidence) {
7
+ return {
8
+ schema_version: SCHEMA_VERSION,
9
+ status: 'no_baseline',
10
+ before_run_id: beforeRunId,
11
+ after_run_id: afterRunId,
12
+ summary: emptySummary(),
13
+ items: [],
14
+ top_improvements: [],
15
+ top_regressions: [],
16
+ top_remaining: buildRemainingItems([], afterEvidence)
17
+ };
18
+ }
19
+
20
+ const beforeItems = indexOpportunities(beforeEvidence.refactoring_opportunities);
21
+ const afterItems = indexOpportunities(afterEvidence?.refactoring_opportunities);
22
+ if (beforeItems.size === 0 && afterItems.size === 0) {
23
+ return {
24
+ schema_version: SCHEMA_VERSION,
25
+ status: 'no_refactoring_opportunities',
26
+ before_run_id: beforeRunId,
27
+ after_run_id: afterRunId,
28
+ summary: emptySummary(),
29
+ items: [],
30
+ top_improvements: [],
31
+ top_regressions: [],
32
+ top_remaining: []
33
+ };
34
+ }
35
+
36
+ const keys = [...new Set([...beforeItems.keys(), ...afterItems.keys()])].sort();
37
+ const items = keys.map((key) => buildDeltaItem(key, beforeItems.get(key), afterItems.get(key)));
38
+ const summary = summarizeItems(items, beforeItems.size, afterItems.size);
39
+ return {
40
+ schema_version: SCHEMA_VERSION,
41
+ status: 'available',
42
+ before_run_id: beforeRunId,
43
+ after_run_id: afterRunId,
44
+ summary,
45
+ items,
46
+ top_improvements: items
47
+ .filter((item) => ['improved', 'removed'].includes(item.status))
48
+ .sort(compareImprovements)
49
+ .slice(0, 10),
50
+ top_regressions: items
51
+ .filter((item) => ['regressed', 'new'].includes(item.status))
52
+ .sort(compareRegressions)
53
+ .slice(0, 10),
54
+ top_remaining: buildRemainingItems(items)
55
+ };
56
+ }
57
+
58
+ export function renderRefactoringDelta(delta, { limit = 10 } = {}) {
59
+ if (!delta || delta.status === 'no_baseline') {
60
+ return `# VibePro リファクタリング差分
61
+
62
+ - status: no_baseline
63
+ - before: -
64
+ - after: ${delta?.after_run_id ?? '-'}
65
+
66
+ 前回の同一Story診断runがないため、差分はまだ算出していません。
67
+ `;
68
+ }
69
+ if (delta.status === 'no_refactoring_opportunities') {
70
+ return `# VibePro リファクタリング差分
71
+
72
+ - status: no_refactoring_opportunities
73
+ - before: ${delta.before_run_id ?? '-'}
74
+ - after: ${delta.after_run_id ?? '-'}
75
+
76
+ 比較対象の両runにリファクタリング機会はありません。
77
+ `;
78
+ }
79
+
80
+ const improvements = delta.top_improvements?.slice(0, limit) ?? [];
81
+ const regressions = delta.top_regressions?.slice(0, limit) ?? [];
82
+ const remaining = delta.top_remaining?.slice(0, limit) ?? [];
83
+ return `# VibePro リファクタリング差分
84
+
85
+ | 項目 | 内容 |
86
+ |------|------|
87
+ | Status | ${delta.status} |
88
+ | Before run | ${delta.before_run_id ?? '-'} |
89
+ | After run | ${delta.after_run_id ?? '-'} |
90
+ | Before機会 | ${delta.summary?.total_before ?? 0}件 |
91
+ | After機会 | ${delta.summary?.total_after ?? 0}件 |
92
+ | 改善 | ${delta.summary?.improved ?? 0}件 |
93
+ | 解消 | ${delta.summary?.removed ?? 0}件 |
94
+ | 悪化 | ${delta.summary?.regressed ?? 0}件 |
95
+ | 新規 | ${delta.summary?.new ?? 0}件 |
96
+
97
+ ## 改善・解消
98
+
99
+ ${renderDeltaTable(improvements)}
100
+
101
+ ## 悪化・新規
102
+
103
+ ${renderDeltaTable(regressions)}
104
+
105
+ ## 残っている上位候補
106
+
107
+ ${renderRemainingTable(remaining)}
108
+ `;
109
+ }
110
+
111
+ export function renderRefactoringDeltaCompact(delta, { limit = 5 } = {}) {
112
+ if (!delta || delta.status === 'no_baseline') {
113
+ return '- 前回の同一Story診断runがないため、差分は未算出';
114
+ }
115
+ if (delta.status === 'no_refactoring_opportunities') {
116
+ return '- 比較対象の両runにリファクタリング機会なし';
117
+ }
118
+ const lines = [
119
+ `- before: ${delta.before_run_id ?? '-'} / after: ${delta.after_run_id ?? '-'}`,
120
+ `- 改善: ${delta.summary?.improved ?? 0}件 / 解消: ${delta.summary?.removed ?? 0}件 / 悪化: ${delta.summary?.regressed ?? 0}件 / 新規: ${delta.summary?.new ?? 0}件`
121
+ ];
122
+ const improvements = delta.top_improvements?.slice(0, limit) ?? [];
123
+ if (improvements.length === 0) {
124
+ lines.push('- 主な改善: なし');
125
+ } else {
126
+ for (const item of improvements) {
127
+ lines.push(`- ${formatDeltaItemLabel(item)}: ${formatCounts(item.before)} -> ${formatCounts(item.after)} (${formatStatus(item.status)})`);
128
+ }
129
+ }
130
+ const remaining = delta.top_remaining?.slice(0, 3) ?? [];
131
+ if (remaining.length > 0) {
132
+ lines.push('- 次の候補:');
133
+ for (const item of remaining) {
134
+ lines.push(` - ${formatDeltaItemLabel(item)}: ${formatCounts(item.after)} (${item.refactoring_intent ?? '-'})`);
135
+ }
136
+ }
137
+ return lines.join('\n');
138
+ }
139
+
140
+ export function formatCounts(counts) {
141
+ return `${counts?.target_file_count ?? 0}ファイル / ${counts?.occurrence_count ?? 0}出現`;
142
+ }
143
+
144
+ function indexOpportunities(opportunities) {
145
+ const indexed = new Map();
146
+ for (const opportunity of Array.isArray(opportunities) ? opportunities : []) {
147
+ const key = buildOpportunityKey(opportunity);
148
+ const existing = indexed.get(key);
149
+ if (!existing) {
150
+ indexed.set(key, normalizeOpportunity(opportunity, key));
151
+ continue;
152
+ }
153
+ indexed.set(key, mergeOpportunity(existing, opportunity));
154
+ }
155
+ return indexed;
156
+ }
157
+
158
+ function buildOpportunityKey(opportunity) {
159
+ const source = opportunity?.source ?? 'unknown';
160
+ const refs = opportunity?.evidence_refs ?? {};
161
+ if (source === 'duplicate_query_shape' && refs.signature) return `${source}:${refs.signature}`;
162
+ if (source === 'responsibility_hotspot' && refs.file) return `${source}:${normalizePath(refs.file)}`;
163
+ const title = opportunity?.title ?? 'untitled';
164
+ const files = uniqueStrings(opportunity?.target_files ?? []).join(',');
165
+ return `${source}:${title}:${files}`;
166
+ }
167
+
168
+ function normalizeOpportunity(opportunity, key) {
169
+ const targetFiles = uniqueStrings(opportunity?.target_files ?? []);
170
+ const refs = opportunity?.evidence_refs ?? {};
171
+ const occurrenceCount = Number.isFinite(refs.occurrence_count)
172
+ ? refs.occurrence_count
173
+ : Number.isFinite(refs.file_count)
174
+ ? refs.file_count
175
+ : targetFiles.length;
176
+ return {
177
+ key,
178
+ source: opportunity?.source ?? null,
179
+ title: opportunity?.title ?? key,
180
+ refactoring_intent: opportunity?.refactoring_intent ?? null,
181
+ finding_id: opportunity?.finding_id ?? null,
182
+ evidence_refs: refs,
183
+ counts: {
184
+ target_file_count: Number.isFinite(opportunity?.target_count) ? opportunity.target_count : targetFiles.length,
185
+ occurrence_count: occurrenceCount,
186
+ rank: opportunity?.rank ?? null,
187
+ score_total: opportunity?.score?.total ?? null
188
+ },
189
+ target_files: targetFiles
190
+ };
191
+ }
192
+
193
+ function mergeOpportunity(existing, opportunity) {
194
+ const next = normalizeOpportunity(opportunity, existing.key);
195
+ const targetFiles = uniqueStrings([...existing.target_files, ...next.target_files]);
196
+ return {
197
+ ...existing,
198
+ title: existing.title ?? next.title,
199
+ counts: {
200
+ target_file_count: targetFiles.length,
201
+ occurrence_count: existing.counts.occurrence_count + next.counts.occurrence_count,
202
+ rank: existing.counts.rank ?? next.counts.rank,
203
+ score_total: (existing.counts.score_total ?? 0) + (next.counts.score_total ?? 0)
204
+ },
205
+ target_files: targetFiles
206
+ };
207
+ }
208
+
209
+ function buildDeltaItem(key, before, after) {
210
+ const beforeCounts = before?.counts ?? zeroCounts();
211
+ const afterCounts = after?.counts ?? zeroCounts();
212
+ const targetFilesBefore = before?.target_files ?? [];
213
+ const targetFilesAfter = after?.target_files ?? [];
214
+ const targetFilesRemoved = targetFilesBefore.filter((file) => !targetFilesAfter.includes(file));
215
+ const targetFilesAdded = targetFilesAfter.filter((file) => !targetFilesBefore.includes(file));
216
+ const occurrenceDelta = afterCounts.occurrence_count - beforeCounts.occurrence_count;
217
+ const targetFileDelta = afterCounts.target_file_count - beforeCounts.target_file_count;
218
+ return {
219
+ key,
220
+ source: after?.source ?? before?.source ?? null,
221
+ title: after?.title ?? before?.title ?? key,
222
+ refactoring_intent: after?.refactoring_intent ?? before?.refactoring_intent ?? null,
223
+ finding_id: after?.finding_id ?? before?.finding_id ?? null,
224
+ before: beforeCounts,
225
+ after: afterCounts,
226
+ occurrence_delta: occurrenceDelta,
227
+ target_file_delta: targetFileDelta,
228
+ target_files_removed: targetFilesRemoved,
229
+ target_files_added: targetFilesAdded,
230
+ target_files_after: targetFilesAfter,
231
+ status: classifyStatus({ before, after, occurrenceDelta, targetFileDelta })
232
+ };
233
+ }
234
+
235
+ function classifyStatus({ before, after, occurrenceDelta, targetFileDelta }) {
236
+ if (before && !after) return 'removed';
237
+ if (!before && after) return 'new';
238
+ if (occurrenceDelta < 0 || targetFileDelta < 0) return 'improved';
239
+ if (occurrenceDelta > 0 || targetFileDelta > 0) return 'regressed';
240
+ return 'unchanged';
241
+ }
242
+
243
+ function summarizeItems(items, totalBefore, totalAfter) {
244
+ return {
245
+ total_before: totalBefore,
246
+ total_after: totalAfter,
247
+ improved: items.filter((item) => item.status === 'improved').length,
248
+ removed: items.filter((item) => item.status === 'removed').length,
249
+ regressed: items.filter((item) => item.status === 'regressed').length,
250
+ new: items.filter((item) => item.status === 'new').length,
251
+ unchanged: items.filter((item) => item.status === 'unchanged').length
252
+ };
253
+ }
254
+
255
+ function emptySummary() {
256
+ return {
257
+ total_before: 0,
258
+ total_after: 0,
259
+ improved: 0,
260
+ removed: 0,
261
+ regressed: 0,
262
+ new: 0,
263
+ unchanged: 0
264
+ };
265
+ }
266
+
267
+ function renderDeltaTable(items) {
268
+ if (!Array.isArray(items) || items.length === 0) return '- なし';
269
+ const rows = items.map((item) => (
270
+ `| ${escapeTable(formatDeltaItemLabel(item))} | ${item.refactoring_intent ?? '-'} | ${formatCounts(item.before)} | ${formatCounts(item.after)} | ${item.target_files_removed.join('<br>') || '-'} | ${formatStatus(item.status)} |`
271
+ ));
272
+ return `| 対象 | Intent | Before | After | 減ったファイル | Status |
273
+ |------|--------|--------|-------|--------------|--------|
274
+ ${rows.join('\n')}`;
275
+ }
276
+
277
+ function renderRemainingTable(items) {
278
+ if (!Array.isArray(items) || items.length === 0) return '- なし';
279
+ const rows = items.map((item) => (
280
+ `| ${escapeTable(formatDeltaItemLabel(item))} | ${item.refactoring_intent ?? '-'} | ${formatCounts(item.after)} | ${item.target_files_after?.slice(0, 6).join('<br>') || '-'} | ${formatStatus(item.status)} |`
281
+ ));
282
+ return `| 対象 | Intent | Current | Files | Delta status |
283
+ |------|--------|---------|-------|--------------|
284
+ ${rows.join('\n')}`;
285
+ }
286
+
287
+ function formatDeltaItemLabel(item) {
288
+ const keyTail = item.key.includes(':') ? item.key.slice(item.key.indexOf(':') + 1) : item.key;
289
+ return item.title && item.title !== item.key ? item.title : keyTail;
290
+ }
291
+
292
+ function formatStatus(status) {
293
+ return {
294
+ improved: '改善',
295
+ removed: '解消',
296
+ regressed: '悪化',
297
+ new: '新規',
298
+ unchanged: '変化なし'
299
+ }[status] ?? status;
300
+ }
301
+
302
+ function compareImprovements(a, b) {
303
+ return improvementMagnitude(b) - improvementMagnitude(a)
304
+ || a.key.localeCompare(b.key);
305
+ }
306
+
307
+ function compareRegressions(a, b) {
308
+ return regressionMagnitude(b) - regressionMagnitude(a)
309
+ || a.key.localeCompare(b.key);
310
+ }
311
+
312
+ function buildRemainingItems(items, afterEvidence = null) {
313
+ if (!Array.isArray(items) || items.length === 0) {
314
+ return [...indexOpportunities(afterEvidence?.refactoring_opportunities).values()]
315
+ .map((item) => ({
316
+ ...item,
317
+ after: item.counts,
318
+ status: 'unchanged',
319
+ target_files_after: item.target_files
320
+ }))
321
+ .sort(compareRemaining)
322
+ .slice(0, 10);
323
+ }
324
+ return items
325
+ .filter((item) => (item.after?.target_file_count ?? 0) > 0 || (item.after?.occurrence_count ?? 0) > 0)
326
+ .sort(compareRemaining)
327
+ .slice(0, 10);
328
+ }
329
+
330
+ function compareRemaining(a, b) {
331
+ const aRank = Number.isFinite(a.after?.rank) ? a.after.rank : Number.MAX_SAFE_INTEGER;
332
+ const bRank = Number.isFinite(b.after?.rank) ? b.after.rank : Number.MAX_SAFE_INTEGER;
333
+ return aRank - bRank
334
+ || (b.after?.score_total ?? 0) - (a.after?.score_total ?? 0)
335
+ || (b.after?.occurrence_count ?? 0) - (a.after?.occurrence_count ?? 0)
336
+ || (b.after?.target_file_count ?? 0) - (a.after?.target_file_count ?? 0)
337
+ || a.key.localeCompare(b.key);
338
+ }
339
+
340
+ function improvementMagnitude(item) {
341
+ return Math.max(0, -item.occurrence_delta) + Math.max(0, -item.target_file_delta);
342
+ }
343
+
344
+ function regressionMagnitude(item) {
345
+ return Math.max(0, item.occurrence_delta) + Math.max(0, item.target_file_delta);
346
+ }
347
+
348
+ function zeroCounts() {
349
+ return {
350
+ target_file_count: 0,
351
+ occurrence_count: 0,
352
+ rank: null,
353
+ score_total: null
354
+ };
355
+ }
356
+
357
+ function uniqueStrings(values) {
358
+ return [...new Set(values.map((value) => normalizePath(value)).filter(Boolean))].sort();
359
+ }
360
+
361
+ function normalizePath(value) {
362
+ return String(value ?? '').replace(/\\/g, '/').replace(/^\.\//, '');
363
+ }
364
+
365
+ function escapeTable(value) {
366
+ return String(value).replace(/\|/g, '\\|');
367
+ }