@esthernandez/vibe-doc 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/dist/checker/index.d.ts +34 -0
- package/dist/checker/index.d.ts.map +1 -0
- package/dist/checker/index.js +154 -0
- package/dist/checker/staleness.d.ts +26 -0
- package/dist/checker/staleness.d.ts.map +1 -0
- package/dist/checker/staleness.js +56 -0
- package/dist/classifier/index.d.ts +26 -0
- package/dist/classifier/index.d.ts.map +1 -0
- package/dist/classifier/index.js +146 -0
- package/dist/classifier/llm-prompt.d.ts +12 -0
- package/dist/classifier/llm-prompt.d.ts.map +1 -0
- package/dist/classifier/llm-prompt.js +123 -0
- package/dist/classifier/scoring-engine.d.ts +41 -0
- package/dist/classifier/scoring-engine.d.ts.map +1 -0
- package/dist/classifier/scoring-engine.js +197 -0
- package/dist/classifier/signals.d.ts +16 -0
- package/dist/classifier/signals.d.ts.map +1 -0
- package/dist/classifier/signals.js +305 -0
- package/dist/gap-analyzer/breadcrumbs.d.ts +18 -0
- package/dist/gap-analyzer/breadcrumbs.d.ts.map +1 -0
- package/dist/gap-analyzer/breadcrumbs.js +314 -0
- package/dist/gap-analyzer/index.d.ts +13 -0
- package/dist/gap-analyzer/index.d.ts.map +1 -0
- package/dist/gap-analyzer/index.js +88 -0
- package/dist/gap-analyzer/matrix.d.ts +29 -0
- package/dist/gap-analyzer/matrix.d.ts.map +1 -0
- package/dist/gap-analyzer/matrix.js +137 -0
- package/dist/gap-analyzer/tier-assigner.d.ts +22 -0
- package/dist/gap-analyzer/tier-assigner.d.ts.map +1 -0
- package/dist/gap-analyzer/tier-assigner.js +112 -0
- package/dist/generator/docx-writer.d.ts +15 -0
- package/dist/generator/docx-writer.d.ts.map +1 -0
- package/dist/generator/docx-writer.js +271 -0
- package/dist/generator/extractor.d.ts +11 -0
- package/dist/generator/extractor.d.ts.map +1 -0
- package/dist/generator/extractor.js +459 -0
- package/dist/generator/index.d.ts +25 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +106 -0
- package/dist/generator/markdown-writer.d.ts +27 -0
- package/dist/generator/markdown-writer.d.ts.map +1 -0
- package/dist/generator/markdown-writer.js +85 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +372 -0
- package/dist/scanner/artifact-scanner.d.ts +16 -0
- package/dist/scanner/artifact-scanner.d.ts.map +1 -0
- package/dist/scanner/artifact-scanner.js +189 -0
- package/dist/scanner/code-scanner.d.ts +17 -0
- package/dist/scanner/code-scanner.d.ts.map +1 -0
- package/dist/scanner/code-scanner.js +69 -0
- package/dist/scanner/file-scanner.d.ts +16 -0
- package/dist/scanner/file-scanner.d.ts.map +1 -0
- package/dist/scanner/file-scanner.js +119 -0
- package/dist/scanner/git-scanner.d.ts +10 -0
- package/dist/scanner/git-scanner.d.ts.map +1 -0
- package/dist/scanner/git-scanner.js +120 -0
- package/dist/scanner/index.d.ts +15 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +106 -0
- package/dist/state/index.d.ts +20 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +141 -0
- package/dist/state/schema.d.ts +101 -0
- package/dist/state/schema.d.ts.map +1 -0
- package/dist/state/schema.js +6 -0
- package/dist/templates/embedded/adr.md +45 -0
- package/dist/templates/embedded/api-spec.md +55 -0
- package/dist/templates/embedded/data-model.md +55 -0
- package/dist/templates/embedded/deployment-procedure.md +63 -0
- package/dist/templates/embedded/runbook.md +55 -0
- package/dist/templates/embedded/test-plan.md +55 -0
- package/dist/templates/embedded/threat-model.md +47 -0
- package/dist/templates/index.d.ts +20 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +106 -0
- package/dist/templates/registry.d.ts +31 -0
- package/dist/templates/registry.d.ts.map +1 -0
- package/dist/templates/registry.js +172 -0
- package/dist/templates/renderer.d.ts +26 -0
- package/dist/templates/renderer.d.ts.map +1 -0
- package/dist/templates/renderer.js +145 -0
- package/dist/utils/language-detect.d.ts +14 -0
- package/dist/utils/language-detect.d.ts.map +1 -0
- package/dist/utils/language-detect.js +58 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +35 -0
- package/dist/versioning/differ.d.ts +20 -0
- package/dist/versioning/differ.d.ts.map +1 -0
- package/dist/versioning/differ.js +160 -0
- package/dist/versioning/index.d.ts +44 -0
- package/dist/versioning/index.d.ts.map +1 -0
- package/dist/versioning/index.js +165 -0
- package/package.json +40 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Markdown Writer Module
|
|
4
|
+
* Writes rendered content to .md files with metadata headers
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.writeMarkdown = writeMarkdown;
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const logger_1 = require("../utils/logger");
|
|
44
|
+
/**
|
|
45
|
+
* Write rendered content to a markdown file with metadata header
|
|
46
|
+
* @param renderedContent - The rendered markdown content
|
|
47
|
+
* @param docType - Type of document (e.g., 'adr', 'runbook')
|
|
48
|
+
* @param projectPath - Root path of the project
|
|
49
|
+
* @param metadata - Document metadata
|
|
50
|
+
* @returns Full path to the written file
|
|
51
|
+
*/
|
|
52
|
+
function writeMarkdown(renderedContent, docType, projectPath, metadata) {
|
|
53
|
+
// Create docs/generated directory
|
|
54
|
+
const docsDir = path.join(projectPath, 'docs', 'generated');
|
|
55
|
+
if (!fs.existsSync(docsDir)) {
|
|
56
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
57
|
+
logger_1.logger.debug('Created docs/generated directory', { path: docsDir });
|
|
58
|
+
}
|
|
59
|
+
// Generate metadata header comment
|
|
60
|
+
const metadataHeader = generateMetadataHeader(metadata, docType);
|
|
61
|
+
// Combine metadata header with content
|
|
62
|
+
const fullContent = metadataHeader + '\n\n' + renderedContent;
|
|
63
|
+
// Write to file
|
|
64
|
+
const outputPath = path.join(docsDir, `${docType}.md`);
|
|
65
|
+
fs.writeFileSync(outputPath, fullContent, 'utf-8');
|
|
66
|
+
logger_1.logger.info('Markdown written', {
|
|
67
|
+
docType,
|
|
68
|
+
path: outputPath,
|
|
69
|
+
size: fullContent.length,
|
|
70
|
+
});
|
|
71
|
+
return outputPath;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate the metadata header comment for a document
|
|
75
|
+
*/
|
|
76
|
+
function generateMetadataHeader(metadata, docType) {
|
|
77
|
+
const lines = [
|
|
78
|
+
'<!-- Generated by Vibe Doc v0.1.0 -->',
|
|
79
|
+
`<!-- Date: ${metadata.generatedAt} -->`,
|
|
80
|
+
`<!-- Classification: ${metadata.classification} -->`,
|
|
81
|
+
`<!-- Source artifacts: ${metadata.sourceArtifacts} files scanned -->`,
|
|
82
|
+
`<!-- Confidence: ${metadata.confidenceSummary.high} high, ${metadata.confidenceSummary.medium} medium, ${metadata.confidenceSummary.low} low sections -->`,
|
|
83
|
+
];
|
|
84
|
+
return lines.join('\n');
|
|
85
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Vibe Doc CLI
|
|
5
|
+
* Main entry point for documentation generation pipeline
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const commander_1 = require("commander");
|
|
44
|
+
const logger_1 = require("./utils/logger");
|
|
45
|
+
const scanner_1 = require("./scanner");
|
|
46
|
+
const state_1 = require("./state");
|
|
47
|
+
const classifier_1 = require("./classifier");
|
|
48
|
+
const gap_analyzer_1 = require("./gap-analyzer");
|
|
49
|
+
const generator_1 = require("./generator");
|
|
50
|
+
const templates_1 = require("./templates");
|
|
51
|
+
const extractor_1 = require("./generator/extractor");
|
|
52
|
+
const program = new commander_1.Command();
|
|
53
|
+
program
|
|
54
|
+
.name('vibe-doc')
|
|
55
|
+
.description('AI-powered documentation gap analyzer for any codebase')
|
|
56
|
+
.version('0.1.0');
|
|
57
|
+
/**
|
|
58
|
+
* Scan command: Run artifact scanner and save inventory
|
|
59
|
+
*/
|
|
60
|
+
program
|
|
61
|
+
.command('scan [projectPath]')
|
|
62
|
+
.description('Scan a project and analyze its artifacts')
|
|
63
|
+
.option('-c, --confidence-threshold <number>', 'Classification confidence threshold (0-1, default: 0.85)', '0.85')
|
|
64
|
+
.option('--profile <path>', 'Path to interview answers JSON file to populate project profile')
|
|
65
|
+
.action(async (projectPath, options) => {
|
|
66
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
67
|
+
const confidenceThreshold = parseFloat(options.confidenceThreshold);
|
|
68
|
+
try {
|
|
69
|
+
logger_1.logger.info('Starting vibe-doc scan', { projectPath: resolvedPath });
|
|
70
|
+
// Initialize state
|
|
71
|
+
let state = (0, state_1.readState)(resolvedPath) || (0, state_1.initState)();
|
|
72
|
+
state.lastScan = new Date().toISOString();
|
|
73
|
+
// Load interview answers from profile if provided
|
|
74
|
+
if (options.profile) {
|
|
75
|
+
try {
|
|
76
|
+
const profilePath = path.resolve(options.profile);
|
|
77
|
+
const profileContent = fs.readFileSync(profilePath, 'utf-8');
|
|
78
|
+
const profileData = JSON.parse(profileContent);
|
|
79
|
+
// Populate interviewAnswers with provided data, using defaults for missing fields
|
|
80
|
+
state.projectProfile.interviewAnswers = {
|
|
81
|
+
projectName: profileData.projectName || '',
|
|
82
|
+
projectDescription: profileData.projectDescription || '',
|
|
83
|
+
mainPurpose: profileData.mainPurpose || '',
|
|
84
|
+
primaryUsers: profileData.primaryUsers || '',
|
|
85
|
+
coreFeatures: profileData.coreFeatures || [],
|
|
86
|
+
technologies: profileData.technologies || [],
|
|
87
|
+
deploymentModel: profileData.deploymentModel || '',
|
|
88
|
+
architectureStyle: profileData.architectureStyle || '',
|
|
89
|
+
};
|
|
90
|
+
// Set providedContext flag
|
|
91
|
+
state.projectProfile.providedContext = 'profile';
|
|
92
|
+
logger_1.logger.info('Loaded interview answers from profile', { path: profilePath });
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
logger_1.logger.warn('Failed to load profile file', { path: options.profile, error });
|
|
96
|
+
// Continue without profile data - not a fatal error
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Run scanner
|
|
100
|
+
logger_1.logger.info('Running artifact inventory scan...');
|
|
101
|
+
state.artifactInventory = await (0, scanner_1.scan)(resolvedPath);
|
|
102
|
+
// Run classifier
|
|
103
|
+
logger_1.logger.info('Running hybrid classification...');
|
|
104
|
+
const classificationResult = (0, classifier_1.classify)(state.artifactInventory, { confidenceThreshold });
|
|
105
|
+
if (classificationResult.resolved) {
|
|
106
|
+
state.classification = classificationResult.classification;
|
|
107
|
+
logger_1.logger.info('Classification complete', {
|
|
108
|
+
category: state.classification.primaryCategory,
|
|
109
|
+
confidence: state.classification.confidence,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Low confidence - use best candidate classification with LLM review available
|
|
114
|
+
state.classification = classificationResult.classification;
|
|
115
|
+
logger_1.logger.info('Low confidence classification, LLM prompt available', {
|
|
116
|
+
category: state.classification.primaryCategory,
|
|
117
|
+
confidence: state.classification.confidence,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Run gap analyzer
|
|
121
|
+
logger_1.logger.info('Running gap analyzer...');
|
|
122
|
+
state.gapReport = (0, gap_analyzer_1.analyzeGaps)(state.classification, state.artifactInventory);
|
|
123
|
+
// Save state
|
|
124
|
+
(0, state_1.writeState)(resolvedPath, state);
|
|
125
|
+
logger_1.logger.info('Scan complete', {
|
|
126
|
+
category: state.classification.primaryCategory,
|
|
127
|
+
docsCovered: state.gapReport.summary.docsCovered,
|
|
128
|
+
docsMissing: state.gapReport.summary.docsMissing,
|
|
129
|
+
coverage: `${state.gapReport.summary.coveragePercent}%`,
|
|
130
|
+
});
|
|
131
|
+
// Output summary
|
|
132
|
+
console.log('\n=== Vibe Doc Scan Complete ===\n');
|
|
133
|
+
console.log(`Project: ${resolvedPath}`);
|
|
134
|
+
console.log(`Category: ${state.classification.primaryCategory}`);
|
|
135
|
+
console.log(`Confidence: ${(state.classification.confidence * 100).toFixed(0)}%`);
|
|
136
|
+
console.log(`\nDocumentation Coverage: ${state.gapReport.summary.coveragePercent}%`);
|
|
137
|
+
console.log(` Covered: ${state.gapReport.summary.docsCovered}`);
|
|
138
|
+
console.log(` Partial: ${state.gapReport.summary.docsPartial}`);
|
|
139
|
+
console.log(` Missing: ${state.gapReport.summary.docsMissing}`);
|
|
140
|
+
console.log(`\nState saved to: ${path.join(resolvedPath, '.vibe-doc', 'state.json')}\n`);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger_1.logger.error('Scan failed', { error });
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* Report command: Display gap analysis report
|
|
149
|
+
*/
|
|
150
|
+
program
|
|
151
|
+
.command('report [projectPath]')
|
|
152
|
+
.description('Display documentation gap report')
|
|
153
|
+
.action((projectPath) => {
|
|
154
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
155
|
+
try {
|
|
156
|
+
const state = (0, state_1.readState)(resolvedPath);
|
|
157
|
+
if (!state) {
|
|
158
|
+
console.error('No vibe-doc state found. Run "vibe-doc scan" first.');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
console.log('\n=== Documentation Gap Report ===\n');
|
|
162
|
+
console.log(`Category: ${state.classification.primaryCategory}`);
|
|
163
|
+
console.log(`Confidence: ${(state.classification.confidence * 100).toFixed(0)}%`);
|
|
164
|
+
console.log(`\nGaps by Tier:\n`);
|
|
165
|
+
const byTier = {
|
|
166
|
+
required: [],
|
|
167
|
+
recommended: [],
|
|
168
|
+
optional: [],
|
|
169
|
+
};
|
|
170
|
+
for (const gap of state.gapReport.gaps) {
|
|
171
|
+
if (!byTier[gap.tier]) {
|
|
172
|
+
byTier[gap.tier] = [];
|
|
173
|
+
}
|
|
174
|
+
byTier[gap.tier].push(gap);
|
|
175
|
+
}
|
|
176
|
+
for (const tier of ['required', 'recommended', 'optional']) {
|
|
177
|
+
const gaps = byTier[tier];
|
|
178
|
+
if (gaps.length > 0) {
|
|
179
|
+
console.log(`${tier.toUpperCase()}:`);
|
|
180
|
+
for (const gap of gaps) {
|
|
181
|
+
const status = gap.found === 0 ? '❌' : gap.missing === 0 ? '✅' : '⚠️';
|
|
182
|
+
console.log(` ${status} ${gap.docType}`);
|
|
183
|
+
console.log(` ${gap.rationale}`);
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
console.log(`Coverage: ${state.gapReport.summary.coveragePercent}%\n`);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
logger_1.logger.error('Report failed', { error });
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
/**
|
|
196
|
+
* Status command: Check current state
|
|
197
|
+
*/
|
|
198
|
+
program
|
|
199
|
+
.command('status [projectPath]')
|
|
200
|
+
.description('Check vibe-doc status')
|
|
201
|
+
.action((projectPath) => {
|
|
202
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
203
|
+
try {
|
|
204
|
+
const state = (0, state_1.readState)(resolvedPath);
|
|
205
|
+
if (!state) {
|
|
206
|
+
console.log('No vibe-doc state found. Run "vibe-doc scan" to initialize.');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
console.log('\n=== Vibe Doc Status ===\n');
|
|
210
|
+
console.log(`Last scan: ${new Date(state.lastScan).toLocaleString()}`);
|
|
211
|
+
console.log(`Artifacts: ${state.artifactInventory.totalArtifacts}`);
|
|
212
|
+
console.log(`Category: ${state.classification.primaryCategory}`);
|
|
213
|
+
console.log(`Coverage: ${state.gapReport.summary.coveragePercent}% (${state.gapReport.summary.docsCovered} covered, ${state.gapReport.summary.docsMissing} missing)\n`);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
logger_1.logger.error('Status check failed', { error });
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
/**
|
|
221
|
+
* Confirm command: Mark classification as user-confirmed
|
|
222
|
+
*/
|
|
223
|
+
program
|
|
224
|
+
.command('confirm [projectPath]')
|
|
225
|
+
.description('Confirm the classification and mark as approved by user')
|
|
226
|
+
.action((projectPath) => {
|
|
227
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
228
|
+
try {
|
|
229
|
+
const state = (0, state_1.readState)(resolvedPath);
|
|
230
|
+
if (!state) {
|
|
231
|
+
console.error('No vibe-doc state found. Run "vibe-doc scan" first.');
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
state.classification.userConfirmed = true;
|
|
235
|
+
(0, state_1.writeState)(resolvedPath, state);
|
|
236
|
+
logger_1.logger.info('Classification confirmed', {
|
|
237
|
+
category: state.classification.primaryCategory,
|
|
238
|
+
});
|
|
239
|
+
console.log('\n=== Classification Confirmed ===\n');
|
|
240
|
+
console.log(`Category "${state.classification.primaryCategory}" has been confirmed by user.\n`);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
logger_1.logger.error('Confirmation failed', { error });
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
/**
|
|
248
|
+
* Check command: CI-safe documentation check
|
|
249
|
+
*/
|
|
250
|
+
program
|
|
251
|
+
.command('check [projectPath]')
|
|
252
|
+
.description('CI-safe documentation check')
|
|
253
|
+
.option('-t, --threshold <commits>', 'Staleness threshold in commits', '20')
|
|
254
|
+
.action(async (projectPath, options) => {
|
|
255
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
256
|
+
const threshold = parseInt(options.threshold, 10);
|
|
257
|
+
try {
|
|
258
|
+
const { runCheck } = await Promise.resolve().then(() => __importStar(require('./checker')));
|
|
259
|
+
const result = await runCheck(resolvedPath, { threshold });
|
|
260
|
+
console.log('\n=== Documentation Check ===\n');
|
|
261
|
+
console.log(result.details);
|
|
262
|
+
console.log();
|
|
263
|
+
if (!result.pass) {
|
|
264
|
+
process.exit(result.exitCode);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
logger_1.logger.error('Check failed', { error });
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
/**
|
|
273
|
+
* Generate command: Generate a document from template
|
|
274
|
+
*/
|
|
275
|
+
program
|
|
276
|
+
.command('generate <docType> [projectPath]')
|
|
277
|
+
.description('Generate a document from template')
|
|
278
|
+
.option('-f, --format <format>', 'Output format: md, docx, or both', 'both')
|
|
279
|
+
.option('-a, --answers <answers.json>', 'Path to answers JSON file')
|
|
280
|
+
.action(async (docType, projectPath, options) => {
|
|
281
|
+
const resolvedPath = projectPath ? path.resolve(projectPath) : process.cwd();
|
|
282
|
+
const format = options.format;
|
|
283
|
+
try {
|
|
284
|
+
logger_1.logger.info('Starting document generation', { docType, format });
|
|
285
|
+
// Read state
|
|
286
|
+
const state = (0, state_1.readState)(resolvedPath);
|
|
287
|
+
if (!state) {
|
|
288
|
+
console.error('No vibe-doc state found. Run "vibe-doc scan" first.');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
// Load answers if provided
|
|
292
|
+
let userAnswers = {};
|
|
293
|
+
if (options.answers) {
|
|
294
|
+
const answersPath = path.resolve(options.answers);
|
|
295
|
+
try {
|
|
296
|
+
const content = fs.readFileSync(answersPath, 'utf-8');
|
|
297
|
+
userAnswers = JSON.parse(content);
|
|
298
|
+
logger_1.logger.debug('Loaded user answers', { path: answersPath });
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
logger_1.logger.warn('Failed to load answers file', { path: answersPath, error });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Extract data from artifacts
|
|
305
|
+
const extractedData = (0, extractor_1.extractDataForDocType)(docType, state, resolvedPath);
|
|
306
|
+
// Build render data
|
|
307
|
+
const renderData = {
|
|
308
|
+
extracted: extractedData,
|
|
309
|
+
user: userAnswers,
|
|
310
|
+
metadata: {
|
|
311
|
+
docType,
|
|
312
|
+
generatedAt: new Date().toISOString(),
|
|
313
|
+
classification: state.classification.primaryCategory,
|
|
314
|
+
sourceArtifacts: state.artifactInventory.totalArtifacts,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
// Generate document
|
|
318
|
+
const result = await (0, generator_1.generateDocument)(docType, resolvedPath, state, renderData, format);
|
|
319
|
+
// Save updated state
|
|
320
|
+
(0, state_1.writeState)(resolvedPath, state);
|
|
321
|
+
// Output summary
|
|
322
|
+
console.log('\n=== Document Generated ===\n');
|
|
323
|
+
console.log(`Document: ${docType}`);
|
|
324
|
+
console.log(`Version: ${result.version}`);
|
|
325
|
+
console.log(`Format(s): ${format}`);
|
|
326
|
+
console.log(`\nGenerated files:`);
|
|
327
|
+
for (const filePath of result.paths) {
|
|
328
|
+
console.log(` ${filePath}`);
|
|
329
|
+
}
|
|
330
|
+
console.log();
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
logger_1.logger.error('Generation failed', { error });
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
/**
|
|
338
|
+
* Templates command group: Manage document templates
|
|
339
|
+
*/
|
|
340
|
+
const templatesCmd = program.command('templates').description('Manage document templates');
|
|
341
|
+
templatesCmd
|
|
342
|
+
.command('list')
|
|
343
|
+
.description('List all available document templates')
|
|
344
|
+
.action(() => {
|
|
345
|
+
try {
|
|
346
|
+
const embeddedTemplates = (0, templates_1.listTemplates)();
|
|
347
|
+
const cacheDir = path.join(process.cwd(), '.vibe-doc');
|
|
348
|
+
const cachedPath = path.join(cacheDir, 'templates');
|
|
349
|
+
console.log('\n=== Available Templates ===\n');
|
|
350
|
+
console.log('EMBEDDED (Built-in):');
|
|
351
|
+
for (const template of embeddedTemplates) {
|
|
352
|
+
console.log(` • ${template}`);
|
|
353
|
+
}
|
|
354
|
+
// Check for cached remote templates
|
|
355
|
+
if (fs.existsSync(cachedPath)) {
|
|
356
|
+
const cachedFiles = fs.readdirSync(cachedPath).filter((f) => f.endsWith('.md'));
|
|
357
|
+
const cachedTemplates = cachedFiles.map((f) => f.replace(/\.md$/, ''));
|
|
358
|
+
const remoteOnly = cachedTemplates.filter((t) => !embeddedTemplates.includes(t));
|
|
359
|
+
if (remoteOnly.length > 0) {
|
|
360
|
+
console.log('\nREMOTE (Cached):');
|
|
361
|
+
for (const template of remoteOnly) {
|
|
362
|
+
console.log(` • ${template}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
logger_1.logger.error('Template list failed', { error });
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact Scanner
|
|
3
|
+
* Reads artifact files and extracts summaries and metadata
|
|
4
|
+
*/
|
|
5
|
+
interface ArtifactMetadata {
|
|
6
|
+
path: string;
|
|
7
|
+
type: 'markdown' | 'skill' | 'config';
|
|
8
|
+
summary: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Enriches artifact files with summaries and metadata
|
|
13
|
+
*/
|
|
14
|
+
export declare function enrichArtifacts(markdownFiles: string[], skillFiles: string[], configFiles: string[]): Promise<ArtifactMetadata[]>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=artifact-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/artifact-scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyID;;GAEG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EAAE,EACvB,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA8B7B"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Artifact Scanner
|
|
4
|
+
* Reads artifact files and extracts summaries and metadata
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.enrichArtifacts = enrichArtifacts;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const logger_1 = require("../utils/logger");
|
|
44
|
+
const YAML_FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---/;
|
|
45
|
+
/**
|
|
46
|
+
* Extracts YAML frontmatter from a file
|
|
47
|
+
*/
|
|
48
|
+
function extractYamlFrontmatter(content) {
|
|
49
|
+
const match = content.match(YAML_FRONTMATTER_REGEX);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const yamlContent = match[1];
|
|
54
|
+
const result = {};
|
|
55
|
+
const lines = yamlContent.split('\n').filter((line) => line.trim());
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
const [key, ...valueParts] = line.split(':');
|
|
58
|
+
const value = valueParts.join(':').trim();
|
|
59
|
+
if (key && value) {
|
|
60
|
+
result[key.trim()] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extracts first meaningful paragraph from markdown content
|
|
67
|
+
*/
|
|
68
|
+
function extractMarkdownSummary(content, maxChars = 200) {
|
|
69
|
+
// Remove frontmatter
|
|
70
|
+
const withoutFrontmatter = content.replace(YAML_FRONTMATTER_REGEX, '').trim();
|
|
71
|
+
// Get lines and skip empty ones
|
|
72
|
+
const lines = withoutFrontmatter.split('\n').filter((line) => line.trim().length > 0);
|
|
73
|
+
let summary = '';
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
// Skip headers, code blocks, etc.
|
|
76
|
+
if (line.startsWith('#') || line.startsWith('```') || line.startsWith(' ')) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
summary += (summary ? ' ' : '') + line.trim();
|
|
80
|
+
if (summary.length >= maxChars) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return summary.substring(0, maxChars).trim();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Reads markdown artifact and returns metadata
|
|
88
|
+
*/
|
|
89
|
+
async function readMarkdownArtifact(filePath) {
|
|
90
|
+
try {
|
|
91
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
92
|
+
// Extract first header as title
|
|
93
|
+
const titleMatch = content.match(/^#\s+(.+?)$/m);
|
|
94
|
+
const title = titleMatch ? titleMatch[1].trim() : undefined;
|
|
95
|
+
// Extract summary
|
|
96
|
+
const summary = extractMarkdownSummary(content);
|
|
97
|
+
return {
|
|
98
|
+
path: filePath,
|
|
99
|
+
type: 'markdown',
|
|
100
|
+
summary,
|
|
101
|
+
title,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
logger_1.logger.warn('Failed to read markdown artifact', { path: filePath, error });
|
|
106
|
+
return {
|
|
107
|
+
path: filePath,
|
|
108
|
+
type: 'markdown',
|
|
109
|
+
summary: '',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Reads skill artifact and returns metadata from YAML frontmatter
|
|
115
|
+
*/
|
|
116
|
+
async function readSkillArtifact(filePath) {
|
|
117
|
+
try {
|
|
118
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
119
|
+
const frontmatter = extractYamlFrontmatter(content);
|
|
120
|
+
const title = frontmatter?.name || path.basename(filePath);
|
|
121
|
+
const summary = frontmatter?.description || '';
|
|
122
|
+
return {
|
|
123
|
+
path: filePath,
|
|
124
|
+
type: 'skill',
|
|
125
|
+
summary,
|
|
126
|
+
title,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
logger_1.logger.warn('Failed to read skill artifact', { path: filePath, error });
|
|
131
|
+
return {
|
|
132
|
+
path: filePath,
|
|
133
|
+
type: 'skill',
|
|
134
|
+
summary: '',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reads config-as-docs artifact
|
|
140
|
+
*/
|
|
141
|
+
async function readConfigArtifact(filePath) {
|
|
142
|
+
try {
|
|
143
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
144
|
+
const summary = extractMarkdownSummary(content, 300);
|
|
145
|
+
const title = path.basename(filePath);
|
|
146
|
+
return {
|
|
147
|
+
path: filePath,
|
|
148
|
+
type: 'config',
|
|
149
|
+
summary,
|
|
150
|
+
title,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
logger_1.logger.warn('Failed to read config artifact', { path: filePath, error });
|
|
155
|
+
return {
|
|
156
|
+
path: filePath,
|
|
157
|
+
type: 'config',
|
|
158
|
+
summary: '',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Enriches artifact files with summaries and metadata
|
|
164
|
+
*/
|
|
165
|
+
async function enrichArtifacts(markdownFiles, skillFiles, configFiles) {
|
|
166
|
+
logger_1.logger.debug('Starting artifact enrichment', {
|
|
167
|
+
markdownCount: markdownFiles.length,
|
|
168
|
+
skillCount: skillFiles.length,
|
|
169
|
+
configCount: configFiles.length,
|
|
170
|
+
});
|
|
171
|
+
const allMetadata = [];
|
|
172
|
+
// Process markdown files
|
|
173
|
+
for (const file of markdownFiles) {
|
|
174
|
+
const metadata = await readMarkdownArtifact(file);
|
|
175
|
+
allMetadata.push(metadata);
|
|
176
|
+
}
|
|
177
|
+
// Process skill files
|
|
178
|
+
for (const file of skillFiles) {
|
|
179
|
+
const metadata = await readSkillArtifact(file);
|
|
180
|
+
allMetadata.push(metadata);
|
|
181
|
+
}
|
|
182
|
+
// Process config files
|
|
183
|
+
for (const file of configFiles) {
|
|
184
|
+
const metadata = await readConfigArtifact(file);
|
|
185
|
+
allMetadata.push(metadata);
|
|
186
|
+
}
|
|
187
|
+
logger_1.logger.info('Artifact enrichment completed', { total: allMetadata.length });
|
|
188
|
+
return allMetadata;
|
|
189
|
+
}
|