wize-dev-kit 0.3.0 → 0.4.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/AGENTS.md +21 -0
- package/ARCH.md +40 -4
- package/CHANGELOG.md +50 -0
- package/DECISIONS.md +13 -0
- package/README.md +11 -2
- package/package.json +3 -2
- package/src/core-skills/module.yaml +4 -0
- package/src/core-skills/wize-spec/assets/headless-schemas.md +39 -0
- package/src/core-skills/wize-spec/assets/spec-template.md +40 -0
- package/src/core-skills/wize-spec/customize.toml +20 -0
- package/src/core-skills/wize-spec/skill.md +110 -0
- package/src/method-skills/1-analysis/wize-document-project/documentation-requirements.csv +12 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/api-contracts-template.md +38 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/architecture-template.md +54 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/component-inventory-template.md +40 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/contribution-guide-template.md +34 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/data-models-template.md +36 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deep-dive-template.md +312 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deployment-guide-template.md +42 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/development-guide-template.md +61 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/index-template.md +185 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-overview-template.md +110 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-scan-report-schema.json +159 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/source-tree-template.md +142 -0
- package/src/method-skills/1-analysis/wize-document-project/workflow.md +23 -0
- package/src/method-skills/1-analysis/wize-domain-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-02-domain-analysis.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-03-competitive-landscape.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-04-regulatory-focus.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-05-technical-trends.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-06-research-synthesis.md +43 -0
- package/src/method-skills/1-analysis/wize-domain-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-domain-research/workflow.md +51 -0
- package/src/method-skills/1-analysis/wize-market-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-market-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-01-init.md +57 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-02-customer-behavior.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-03-customer-pain-points.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-04-customer-decisions.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-05-competitive-analysis.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-06-research-completion.md +47 -0
- package/src/method-skills/1-analysis/wize-market-research/workflow.md +59 -0
- package/src/method-skills/1-analysis/wize-technical-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-technical-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-02-technical-overview.md +45 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-03-integration-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-04-architectural-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-05-implementation-research.md +42 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-06-research-synthesis.md +46 -0
- package/src/method-skills/1-analysis/wize-technical-research/workflow.md +53 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/architecture-decision-template.md +41 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/customize.toml +20 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01-init.md +95 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01b-continue.md +32 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-02-context.md +126 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-03-starter.md +110 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-04-decisions.md +129 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-05-patterns.md +109 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-06-structure.md +97 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-07-validation.md +124 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-08-complete.md +59 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/workflow.md +38 -201
- package/src/method-skills/4-implementation/wize-code-review/customize.toml +21 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-01-gather-context.md +64 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-02-review.md +38 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-03-triage.md +43 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-04-present.md +100 -0
- package/src/method-skills/4-implementation/wize-code-review/workflow.md +34 -89
- package/src/method-skills/module.yaml +19 -0
- package/src/tea-skills/wize-tea-risk/workflow.md +11 -0
- package/tools/installer/commands/doctor.js +27 -1
- package/tools/installer/commands/document-project.js +93 -0
- package/tools/installer/document-project/batch-scanner.js +93 -0
- package/tools/installer/document-project/classify.js +170 -0
- package/tools/installer/document-project/modes/deep-dive.js +196 -0
- package/tools/installer/document-project/modes/full-rescan.js +15 -0
- package/tools/installer/document-project/modes/initial-scan.js +100 -0
- package/tools/installer/document-project/modes/quick.js +211 -0
- package/tools/installer/document-project/render-index.js +101 -0
- package/tools/installer/document-project/state.js +110 -0
- package/tools/installer/wize-cli.js +46 -30
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Deep-dive mode for `wize-dev-kit document-project`.
|
|
2
|
+
//
|
|
3
|
+
// Exhaustive documentation of a specific folder/file/feature.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const { batchScanner, scanFolder } = require('../batch-scanner.js');
|
|
10
|
+
const { updateState } = require('../state.js');
|
|
11
|
+
const { renderIndex } = require('../render-index.js');
|
|
12
|
+
|
|
13
|
+
function sanitize(name) {
|
|
14
|
+
return name.replace(/[^a-z0-9_-]/gi, '-').toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function walkAllFiles(rootDir, ignore = ['node_modules', '.git', 'dist', 'build', 'coverage']) {
|
|
18
|
+
const files = [];
|
|
19
|
+
const stack = [rootDir];
|
|
20
|
+
while (stack.length) {
|
|
21
|
+
const dir = stack.pop();
|
|
22
|
+
let entries;
|
|
23
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { continue; }
|
|
24
|
+
for (const e of entries) {
|
|
25
|
+
const full = path.join(dir, e.name);
|
|
26
|
+
if (e.isDirectory()) {
|
|
27
|
+
if (!ignore.includes(e.name)) stack.push(full);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (e.isFile() && /\.(js|ts|jsx|tsx|mjs|cjs)$/.test(e.name)) files.push(full);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return files;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function matchesTarget(filePath, projectRoot, targetType, targetName) {
|
|
37
|
+
const rel = path.relative(projectRoot, filePath).toLowerCase();
|
|
38
|
+
const name = targetName.toLowerCase();
|
|
39
|
+
if (targetType === 'feature') {
|
|
40
|
+
return rel.includes(name);
|
|
41
|
+
}
|
|
42
|
+
if (targetType === 'api_group') {
|
|
43
|
+
const inApiFolder = /\/(routes|api|controllers|endpoints)\//.test(rel);
|
|
44
|
+
return inApiFolder && rel.includes(name);
|
|
45
|
+
}
|
|
46
|
+
if (targetType === 'component_group') {
|
|
47
|
+
const inComponentFolder = /\/(components|ui|widgets)\//.test(rel);
|
|
48
|
+
return inComponentFolder && rel.includes(name);
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveTarget(projectRoot, target) {
|
|
54
|
+
if (!target) return { targetType: null, targetName: null, targetPath: null, files: [] };
|
|
55
|
+
|
|
56
|
+
// Explicit typed target: "feature:auth", "api:users", "component:Button"
|
|
57
|
+
const typedMatch = target.match(/^([a-z_]+):(.+)$/i);
|
|
58
|
+
if (typedMatch) {
|
|
59
|
+
const targetType = typedMatch[1].toLowerCase();
|
|
60
|
+
const targetName = typedMatch[2];
|
|
61
|
+
if (['feature', 'api_group', 'component_group'].includes(targetType)) {
|
|
62
|
+
const all = walkAllFiles(projectRoot);
|
|
63
|
+
const files = all.filter(f => matchesTarget(f, projectRoot, targetType, targetName));
|
|
64
|
+
return { targetType, targetName, targetPath: target, files };
|
|
65
|
+
}
|
|
66
|
+
return { targetType, targetName, targetPath: target, files: [], error: `unknown target type: ${targetType}` };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Path-based target (folder or file)
|
|
70
|
+
const targetPath = path.resolve(projectRoot, target);
|
|
71
|
+
if (fs.existsSync(targetPath)) {
|
|
72
|
+
const stats = fs.statSync(targetPath);
|
|
73
|
+
const targetType = stats.isFile() ? 'file' : 'folder';
|
|
74
|
+
const files = stats.isFile() ? [targetPath] : scanFolder(targetPath, { ignore: ['node_modules', '.git', 'dist', 'build', 'coverage'] }).files.map(f => f.path);
|
|
75
|
+
return { targetType, targetName: path.basename(targetPath), targetPath, files };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Bare name treated as feature search
|
|
79
|
+
const all = walkAllFiles(projectRoot);
|
|
80
|
+
const files = all.filter(f => matchesTarget(f, projectRoot, 'feature', target));
|
|
81
|
+
return { targetType: 'feature', targetName: target, targetPath: target, files };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function listFilesInScope(rootDir, targetPath) {
|
|
85
|
+
const stats = fs.statSync(targetPath);
|
|
86
|
+
if (stats.isFile()) return [targetPath];
|
|
87
|
+
const scan = scanFolder(targetPath, { ignore: ['node_modules', '.git', 'dist', 'build', 'coverage'] });
|
|
88
|
+
return scan.files.map(f => f.path);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readFileSummary(filePath) {
|
|
92
|
+
try {
|
|
93
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
94
|
+
const lines = content.split('\n');
|
|
95
|
+
const exports = [];
|
|
96
|
+
const todos = [];
|
|
97
|
+
for (let i = 0; i < lines.length; i++) {
|
|
98
|
+
const line = lines[i];
|
|
99
|
+
if (/\b(module\.exports|exports\.|export\s+(default\s+)?|function\s+|class\s+)/.test(line)) {
|
|
100
|
+
exports.push(line.trim().slice(0, 80));
|
|
101
|
+
}
|
|
102
|
+
if (/\b(TODO|FIXME|HACK|XXX)\b/i.test(line)) {
|
|
103
|
+
todos.push(`${i + 1}: ${line.trim().slice(0, 80)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
path: filePath,
|
|
108
|
+
loc: lines.length,
|
|
109
|
+
exports: exports.slice(0, 10),
|
|
110
|
+
todos: todos.slice(0, 10)
|
|
111
|
+
};
|
|
112
|
+
} catch {
|
|
113
|
+
return { path: filePath, loc: 0, exports: [], todos: [] };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function runDeepDive(projectRoot, { target, log = console.log } = {}) {
|
|
118
|
+
if (!target) {
|
|
119
|
+
return { ok: false, error: 'missing --target for deep_dive', exitCode: 1 };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const resolved = resolveTarget(projectRoot, target);
|
|
123
|
+
if (resolved.error) {
|
|
124
|
+
return { ok: false, error: resolved.error, exitCode: 1 };
|
|
125
|
+
}
|
|
126
|
+
if (resolved.targetType === 'file' || resolved.targetType === 'folder') {
|
|
127
|
+
if (!fs.existsSync(resolved.targetPath)) {
|
|
128
|
+
return { ok: false, error: `target not found: ${target}`, exitCode: 1 };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (resolved.files.length === 0) {
|
|
132
|
+
return { ok: false, error: `target not found or no files matched: ${target}`, exitCode: 1 };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const files = resolved.files;
|
|
136
|
+
const summaries = files.map(readFileSummary);
|
|
137
|
+
const targetName = resolved.targetName;
|
|
138
|
+
const outputName = `deep-dive-${sanitize(targetName)}.md`;
|
|
139
|
+
const outputPath = path.join(projectRoot, '.wize', 'knowledge', 'document-project', outputName);
|
|
140
|
+
|
|
141
|
+
const lines = [
|
|
142
|
+
'---',
|
|
143
|
+
'status: baseline',
|
|
144
|
+
'owner: Pepper Potts + Tony Stark',
|
|
145
|
+
`created: ${new Date().toISOString().slice(0, 10)}`,
|
|
146
|
+
`last_refreshed: ${new Date().toISOString().slice(0, 10)}`,
|
|
147
|
+
'---',
|
|
148
|
+
'',
|
|
149
|
+
`# Deep Dive — ${targetName}`,
|
|
150
|
+
'',
|
|
151
|
+
`**Type:** ${resolved.targetType}`,
|
|
152
|
+
`**Scope:** ${target}`,
|
|
153
|
+
`**Files analyzed:** ${files.length}`,
|
|
154
|
+
'',
|
|
155
|
+
'## File Inventory',
|
|
156
|
+
''
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
for (const s of summaries) {
|
|
160
|
+
lines.push(`### ${path.relative(projectRoot, s.path)}`);
|
|
161
|
+
lines.push(`- **LOC:** ${s.loc}`);
|
|
162
|
+
if (s.exports.length) {
|
|
163
|
+
lines.push('- **Exports/signatures:**');
|
|
164
|
+
for (const e of s.exports) lines.push(` - \`${e}\``);
|
|
165
|
+
}
|
|
166
|
+
if (s.todos.length) {
|
|
167
|
+
lines.push('- **TODOs/FIXMEs:**');
|
|
168
|
+
for (const t of s.todos) lines.push(` - ${t}`);
|
|
169
|
+
}
|
|
170
|
+
lines.push('');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
lines.push('## Modification Guidance', '', '- Verify tests before changing exported signatures.', '');
|
|
174
|
+
|
|
175
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
176
|
+
fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8');
|
|
177
|
+
|
|
178
|
+
updateState(projectRoot, {
|
|
179
|
+
deep_dive_targets: [{
|
|
180
|
+
target_name: targetName,
|
|
181
|
+
target_path: resolved.targetPath,
|
|
182
|
+
files_analyzed: files.length,
|
|
183
|
+
output_file: outputName,
|
|
184
|
+
timestamp: new Date().toISOString()
|
|
185
|
+
}],
|
|
186
|
+
outputs_generated: [outputName]
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Update master index with a link to this deep-dive doc.
|
|
190
|
+
renderIndex(projectRoot, { deepDiveFiles: [outputName] });
|
|
191
|
+
|
|
192
|
+
log(`Deep-dive written to ${outputPath}`);
|
|
193
|
+
return { ok: true, mode: 'deep_dive', changed: true, written: [outputName] };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = { runDeepDive, listFilesInScope, readFileSummary, resolveTarget };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Full rescan mode for `wize-dev-kit document-project`.
|
|
2
|
+
//
|
|
3
|
+
// Archives the previous state and re-runs an initial scan from scratch.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { archiveOldState } = require('../state.js');
|
|
8
|
+
const { runInitialScan } = require('./initial-scan.js');
|
|
9
|
+
|
|
10
|
+
function runFullRescan(projectRoot, options = {}) {
|
|
11
|
+
archiveOldState(projectRoot);
|
|
12
|
+
return runInitialScan(projectRoot, { ...options, mode: 'full_rescan' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { runFullRescan };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Initial scan mode for `wize-dev-kit document-project`.
|
|
2
|
+
//
|
|
3
|
+
// Runs project-type classification, conditional scans, and generates index + docs.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const { classifyProject } = require('../classify.js');
|
|
10
|
+
const { batchScanner } = require('../batch-scanner.js');
|
|
11
|
+
const { renderIndex } = require('../render-index.js');
|
|
12
|
+
const { initState, updateState } = require('../state.js');
|
|
13
|
+
const { runQuick } = require('./quick.js');
|
|
14
|
+
|
|
15
|
+
function now() {
|
|
16
|
+
return new Date().toISOString().slice(0, 10);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readRequirements(row) {
|
|
20
|
+
return {
|
|
21
|
+
api: row.requires_api_scan,
|
|
22
|
+
data: row.requires_data_models,
|
|
23
|
+
state: row.requires_state_management,
|
|
24
|
+
ui: row.requires_ui_components,
|
|
25
|
+
deploy: row.requires_deployment_config,
|
|
26
|
+
hardware: row.requires_hardware_docs,
|
|
27
|
+
assets: row.requires_asset_inventory
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function existingDocs(root) {
|
|
32
|
+
const candidates = [
|
|
33
|
+
'README.md',
|
|
34
|
+
'CONTRIBUTING.md',
|
|
35
|
+
'ARCHITECTURE.md',
|
|
36
|
+
'DEPLOYMENT.md',
|
|
37
|
+
'API.md'
|
|
38
|
+
];
|
|
39
|
+
return candidates.filter(f => fs.existsSync(path.join(root, f))).map(f => path.join(root, f));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function runInitialScan(projectRoot, { scanLevel = 'quick', log = console.log, csvPath } = {}) {
|
|
43
|
+
const classification = classifyProject(projectRoot, csvPath ? { csvPath } : {});
|
|
44
|
+
const state = initState(projectRoot, 'initial_scan', scanLevel);
|
|
45
|
+
|
|
46
|
+
updateState(projectRoot, {
|
|
47
|
+
completed_steps: [{ step: 'classify', status: 'completed', timestamp: new Date().toISOString(), summary: `Classified as ${classification.projectTypes.join(', ')}` }],
|
|
48
|
+
current_step: 'scan',
|
|
49
|
+
findings: {
|
|
50
|
+
project_classification: {
|
|
51
|
+
repository_type: classification.parts.length >= 2 ? 'multi-part' : 'monolith',
|
|
52
|
+
parts_count: classification.parts.length,
|
|
53
|
+
primary_language: 'unknown',
|
|
54
|
+
architecture_type: classification.projectTypes[0] || 'unknown'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
let generated = [];
|
|
60
|
+
if (scanLevel === 'deep' || scanLevel === 'exhaustive') {
|
|
61
|
+
const batches = batchScanner(projectRoot, { ignore: ['.wize', '.git', 'node_modules'] });
|
|
62
|
+
updateState(projectRoot, {
|
|
63
|
+
completed_steps: [{ step: 'scan', status: 'completed', timestamp: new Date().toISOString(), summary: `${batches.length} batches scanned` }],
|
|
64
|
+
current_step: 'generate',
|
|
65
|
+
findings: {
|
|
66
|
+
batches_completed: batches.map(b => ({ path: b.folder, files_scanned: b.fileCount, summary: `${b.totalLoc} LOC` }))
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Always write baseline + index.
|
|
72
|
+
const quick = runQuick(projectRoot, { log });
|
|
73
|
+
generated.push(...quick.written.map(f => path.join(projectRoot, '.wize', 'knowledge', 'document-project', f)));
|
|
74
|
+
|
|
75
|
+
const indexResult = renderIndex(projectRoot, {
|
|
76
|
+
projectTypes: classification.projectTypes,
|
|
77
|
+
parts: classification.parts,
|
|
78
|
+
generated,
|
|
79
|
+
existing: existingDocs(projectRoot)
|
|
80
|
+
});
|
|
81
|
+
generated.push(path.join(projectRoot, '.wize', 'knowledge', 'document-project', 'index.md'));
|
|
82
|
+
|
|
83
|
+
updateState(projectRoot, {
|
|
84
|
+
completed_steps: [{ step: 'generate', status: 'completed', timestamp: new Date().toISOString(), summary: `Generated ${generated.length} files` }],
|
|
85
|
+
current_step: 'completed',
|
|
86
|
+
outputs_generated: generated.map(g => path.basename(g)),
|
|
87
|
+
resume_instructions: 'Scan complete'
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
changed: true,
|
|
92
|
+
mode: 'initial_scan',
|
|
93
|
+
scanLevel,
|
|
94
|
+
projectTypes: classification.projectTypes,
|
|
95
|
+
parts: classification.parts,
|
|
96
|
+
generated: generated.map(g => path.basename(g))
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { runInitialScan };
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// Quick baseline mode for `wize-dev-kit document-project`.
|
|
2
|
+
//
|
|
3
|
+
// Produces the 6 lightweight baseline files. Does not read source files.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
const BASELINE_FILES = {
|
|
11
|
+
'overview.md': overviewMarkdown,
|
|
12
|
+
'architecture-snapshot.md': architectureMarkdown,
|
|
13
|
+
'conventions.md': conventionsMarkdown,
|
|
14
|
+
'dependencies.md': dependenciesMarkdown,
|
|
15
|
+
'risk-spots.md': riskMarkdown,
|
|
16
|
+
'open-questions.md': questionsMarkdown
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function now() {
|
|
20
|
+
return new Date().toISOString().slice(0, 10);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readPackage(root) {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function countLines(root) {
|
|
32
|
+
let total = 0;
|
|
33
|
+
const dirs = ['src', 'tools', 'adapters', 'schemas', 'test'];
|
|
34
|
+
for (const dir of dirs) {
|
|
35
|
+
const full = path.join(root, dir);
|
|
36
|
+
if (!fs.existsSync(full)) continue;
|
|
37
|
+
total += walkLines(full);
|
|
38
|
+
}
|
|
39
|
+
return total;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function walkLines(dir) {
|
|
43
|
+
let total = 0;
|
|
44
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
45
|
+
for (const e of entries) {
|
|
46
|
+
const full = path.join(dir, e.name);
|
|
47
|
+
if (e.isDirectory()) total += walkLines(full);
|
|
48
|
+
else if (e.isFile() && /\.(js|md|yaml|json)$/.test(e.name)) {
|
|
49
|
+
const content = fs.readFileSync(full, 'utf-8');
|
|
50
|
+
total += content.split('\n').length;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return total;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function recentCommitCount(root) {
|
|
57
|
+
// Avoid shelling out; return 0 if not a git repo or git unavailable.
|
|
58
|
+
// Caller can overlay a real count from CLI.
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function overviewMarkdown(root, pkg) {
|
|
63
|
+
return `---
|
|
64
|
+
status: baseline
|
|
65
|
+
owner: Pepper Potts + Peggy Carter
|
|
66
|
+
created: ${now()}
|
|
67
|
+
last_refreshed: ${now()}
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
# Overview
|
|
71
|
+
|
|
72
|
+
**Project:** ${pkg.name || path.basename(root)}
|
|
73
|
+
**What it is:** ${pkg.description || 'Project documented by Wize Dev Kit.'}
|
|
74
|
+
**Current version:** ${pkg.version || 'unknown'}
|
|
75
|
+
|
|
76
|
+
## Size
|
|
77
|
+
|
|
78
|
+
- Source lines of code: ~${countLines(root)} LOC.
|
|
79
|
+
- Runtime dependencies: ${Object.keys(pkg.dependencies || {}).length}.
|
|
80
|
+
- Dev dependencies: ${Object.keys(pkg.devDependencies || {}).length}.
|
|
81
|
+
|
|
82
|
+
## What it ships
|
|
83
|
+
|
|
84
|
+
- See architecture-snapshot.md for components and entry points.
|
|
85
|
+
- See conventions.md for coding patterns.
|
|
86
|
+
- See dependencies.md for runtime and dev dependency roles.
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function architectureMarkdown() {
|
|
91
|
+
return `---
|
|
92
|
+
status: baseline
|
|
93
|
+
owner: Pepper Potts + Tony Stark
|
|
94
|
+
created: ${now()}
|
|
95
|
+
last_refreshed: ${now()}
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
# Architecture Snapshot
|
|
99
|
+
|
|
100
|
+
## Entry points
|
|
101
|
+
|
|
102
|
+
- Documented after a deeper scan (initial_scan mode).
|
|
103
|
+
|
|
104
|
+
## Components
|
|
105
|
+
|
|
106
|
+
- Run \`wize-dev-kit document-project initial_scan deep\` for component-level detail.
|
|
107
|
+
|
|
108
|
+
## Integration surface
|
|
109
|
+
|
|
110
|
+
- See dependencies.md for external integrations.
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function conventionsMarkdown() {
|
|
115
|
+
return `---
|
|
116
|
+
status: baseline
|
|
117
|
+
owner: Peggy Carter
|
|
118
|
+
created: ${now()}
|
|
119
|
+
last_refreshed: ${now()}
|
|
120
|
+
sampled: "pending deeper scan"
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Conventions (observed, not prescribed)
|
|
124
|
+
|
|
125
|
+
## Quick notes
|
|
126
|
+
|
|
127
|
+
- Run \`wize-dev-kit document-project initial_scan deep\` to sample files and fill this doc.
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function dependenciesMarkdown(root, pkg) {
|
|
132
|
+
const runtime = Object.entries(pkg.dependencies || {})
|
|
133
|
+
.map(([name, version]) => `| ${name} | ${version} | | |`).join('\n');
|
|
134
|
+
const dev = Object.entries(pkg.devDependencies || {})
|
|
135
|
+
.map(([name, version]) => `| ${name} | ${version} | | |`).join('\n');
|
|
136
|
+
|
|
137
|
+
return `---
|
|
138
|
+
status: baseline
|
|
139
|
+
owner: Pepper Potts
|
|
140
|
+
created: ${now()}
|
|
141
|
+
last_refreshed: ${now()}
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
# Dependencies
|
|
145
|
+
|
|
146
|
+
## Runtime
|
|
147
|
+
|
|
148
|
+
| Name | Version | Role in this repo | Load-bearing? |
|
|
149
|
+
|---|---|---|---|
|
|
150
|
+
${runtime || '| — | — | — | — |'}
|
|
151
|
+
|
|
152
|
+
## Dev / bundled
|
|
153
|
+
|
|
154
|
+
| Name | Version | Role in this repo | Load-bearing? |
|
|
155
|
+
|---|---|---|---|
|
|
156
|
+
${dev || '| — | — | — | — |'}
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function riskMarkdown() {
|
|
161
|
+
return `---
|
|
162
|
+
status: baseline
|
|
163
|
+
owner: Pepper Potts + Tony Stark
|
|
164
|
+
created: ${now()}
|
|
165
|
+
last_refreshed: ${now()}
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# Risk Spots
|
|
169
|
+
|
|
170
|
+
| Area | Symptom | Likely cause | Confidence |
|
|
171
|
+
|---|---|---|---|
|
|
172
|
+
| | | | |
|
|
173
|
+
|
|
174
|
+
Run \`wize-dev-kit document-project initial_scan deep\` to populate risk spots.
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function questionsMarkdown() {
|
|
179
|
+
return `---
|
|
180
|
+
status: baseline
|
|
181
|
+
owner: Pepper Potts + Peggy Carter
|
|
182
|
+
created: ${now()}
|
|
183
|
+
last_refreshed: ${now()}
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
# Open Questions
|
|
187
|
+
|
|
188
|
+
| Question | Why it matters | Owner to ask |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| | | |
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function runQuick(projectRoot, opts = {}) {
|
|
195
|
+
const pkg = readPackage(projectRoot);
|
|
196
|
+
const dir = path.join(projectRoot, '.wize', 'knowledge', 'document-project');
|
|
197
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
198
|
+
|
|
199
|
+
const written = [];
|
|
200
|
+
for (const [name, factory] of Object.entries(BASELINE_FILES)) {
|
|
201
|
+
const file = path.join(dir, name);
|
|
202
|
+
const content = factory(projectRoot, pkg);
|
|
203
|
+
fs.writeFileSync(file, content, 'utf-8');
|
|
204
|
+
written.push(name);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (opts.log) opts.log(`Quick baseline written to ${dir}`);
|
|
208
|
+
return { changed: true, mode: 'quick', written };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = { runQuick, BASELINE_FILES, countLines };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Master index renderer for `wize-dev-kit document-project`.
|
|
2
|
+
//
|
|
3
|
+
// Generates `.wize/knowledge/document-project/index.md` linking all docs.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
function link(name, file) {
|
|
11
|
+
return `- [${name}](./${file})`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function linkWithMarker(name, file, exists) {
|
|
15
|
+
const base = link(name, file);
|
|
16
|
+
return exists ? base : `${base} _(To be generated)_`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function renderIndex(projectRoot, { projectTypes = [], parts = [], generated = [], existing = [], deepDiveFiles = [] } = {}) {
|
|
20
|
+
const dir = path.join(projectRoot, '.wize', 'knowledge', 'document-project');
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
|
|
23
|
+
const generatedSet = new Set(generated.map(g => path.basename(g)));
|
|
24
|
+
const existingSet = new Set(existing.map(e => path.basename(e)));
|
|
25
|
+
const has = name => generatedSet.has(name) || existingSet.has(name);
|
|
26
|
+
|
|
27
|
+
const isMultiPart = parts.length >= 2;
|
|
28
|
+
const primaryType = projectTypes[0] || 'unknown';
|
|
29
|
+
|
|
30
|
+
const lines = [
|
|
31
|
+
'---',
|
|
32
|
+
'status: baseline',
|
|
33
|
+
'owner: Pepper Potts + Peggy Carter',
|
|
34
|
+
`created: ${new Date().toISOString().slice(0, 10)}`,
|
|
35
|
+
`last_refreshed: ${new Date().toISOString().slice(0, 10)}`,
|
|
36
|
+
'---',
|
|
37
|
+
'',
|
|
38
|
+
'# Project Documentation Index',
|
|
39
|
+
'',
|
|
40
|
+
`**Type:** ${isMultiPart ? 'multi-part' : primaryType}`,
|
|
41
|
+
`**Primary project type(s):** ${projectTypes.join(', ') || 'unknown'}`,
|
|
42
|
+
'',
|
|
43
|
+
'## Generated Documentation',
|
|
44
|
+
'',
|
|
45
|
+
link('Project Overview', 'project-overview.md'),
|
|
46
|
+
linkWithMarker('Source Tree Analysis', 'source-tree-analysis.md', has('source-tree-analysis.md')),
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
if (isMultiPart) {
|
|
50
|
+
for (const part of parts) {
|
|
51
|
+
const suffix = part.part_id === 'root' ? '' : `-${part.part_id}`;
|
|
52
|
+
lines.push(linkWithMarker(`Architecture — ${part.part_id || 'root'}`, `architecture${suffix}.md`, has(`architecture${suffix}.md`)));
|
|
53
|
+
lines.push(linkWithMarker(`Development Guide — ${part.part_id || 'root'}`, `development-guide${suffix}.md`, has(`development-guide${suffix}.md`)));
|
|
54
|
+
lines.push(linkWithMarker(`Component Inventory — ${part.part_id || 'root'}`, `component-inventory${suffix}.md`, has(`component-inventory${suffix}.md`)));
|
|
55
|
+
lines.push(linkWithMarker(`API Contracts — ${part.part_id || 'root'}`, `api-contracts${suffix}.md`, has(`api-contracts${suffix}.md`)));
|
|
56
|
+
lines.push(linkWithMarker(`Data Models — ${part.part_id || 'root'}`, `data-models${suffix}.md`, has(`data-models${suffix}.md`)));
|
|
57
|
+
}
|
|
58
|
+
lines.push(linkWithMarker('Integration Architecture', 'integration-architecture.md', has('integration-architecture.md')));
|
|
59
|
+
} else {
|
|
60
|
+
lines.push(linkWithMarker('Architecture', 'architecture.md', has('architecture.md')));
|
|
61
|
+
lines.push(linkWithMarker('Development Guide', 'development-guide.md', has('development-guide.md')));
|
|
62
|
+
lines.push(linkWithMarker('Component Inventory', 'component-inventory.md', has('component-inventory.md')));
|
|
63
|
+
lines.push(linkWithMarker('API Contracts', 'api-contracts.md', has('api-contracts.md')));
|
|
64
|
+
lines.push(linkWithMarker('Data Models', 'data-models.md', has('data-models.md')));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
lines.push(linkWithMarker('Deployment Guide', 'deployment-guide.md', has('deployment-guide.md')));
|
|
68
|
+
lines.push(linkWithMarker('Contribution Guide', 'contribution-guide.md', has('contribution-guide.md')));
|
|
69
|
+
if (deepDiveFiles.length) {
|
|
70
|
+
lines.push('', '## Deep-Dive Docs', '');
|
|
71
|
+
for (const f of deepDiveFiles) {
|
|
72
|
+
lines.push(link(f.replace(/^deep-dive-/, '').replace(/\.md$/, ''), f));
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
lines.push(linkWithMarker('Deep-Dive Docs', 'deep-dive/', false)); // directory marker
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (existing.length) {
|
|
79
|
+
lines.push('', '## Existing Documentation', '');
|
|
80
|
+
for (const doc of existing) {
|
|
81
|
+
const rel = path.relative(dir, doc);
|
|
82
|
+
lines.push(`- [${path.basename(doc)}](./${rel})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
lines.push('', '## Brownfield Baseline', '');
|
|
87
|
+
lines.push(link('Overview', 'overview.md'));
|
|
88
|
+
lines.push(link('Architecture Snapshot', 'architecture-snapshot.md'));
|
|
89
|
+
lines.push(link('Conventions', 'conventions.md'));
|
|
90
|
+
lines.push(link('Dependencies', 'dependencies.md'));
|
|
91
|
+
lines.push(link('Risk Spots', 'risk-spots.md'));
|
|
92
|
+
lines.push(link('Open Questions', 'open-questions.md'));
|
|
93
|
+
|
|
94
|
+
lines.push('', '---', '', '_Generated by `wize-document-project`._', '');
|
|
95
|
+
|
|
96
|
+
const file = path.join(dir, 'index.md');
|
|
97
|
+
fs.writeFileSync(file, lines.join('\n'), 'utf-8');
|
|
98
|
+
return { changed: true, written: ['index.md'] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = { renderIndex, link, linkWithMarker };
|