@doquflow/server 1.6.0 → 2.0.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.
@@ -1,111 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildGraph = buildGraph;
4
- const list_wiki_1 = require("./list-wiki");
5
- const SEP = "\u0000";
6
- async function buildGraph(input) {
7
- const generatedAt = new Date().toISOString();
8
- try {
9
- const wiki = await (0, list_wiki_1.listWiki)({ project_path: input.project_path });
10
- if (wiki.error) {
11
- return {
12
- nodes: [],
13
- edges: [],
14
- meta: {
15
- total_pages: 0, total_edges: 0, orphans: 0, isolated: 0,
16
- dangling_refs: [], generated_at: generatedAt,
17
- },
18
- error: wiki.error,
19
- };
20
- }
21
- const pages = wiki.pages;
22
- const nodeIds = new Set(pages.map((p) => p.id));
23
- const edgeMap = new Map();
24
- const dangling = new Set();
25
- // Pass 1: outbound
26
- for (const page of pages) {
27
- const src = page.id;
28
- for (const tgt of page.outbound_links ?? []) {
29
- if (tgt === src)
30
- continue;
31
- if (!nodeIds.has(tgt)) {
32
- dangling.add(tgt);
33
- continue;
34
- }
35
- const key = `${src}${SEP}${tgt}`;
36
- if (!edgeMap.has(key)) {
37
- edgeMap.set(key, { source: src, target: tgt, kind: "outbound" });
38
- }
39
- }
40
- }
41
- // Pass 2: inbound — `n.inbound_links` lists pages that point AT n.
42
- for (const page of pages) {
43
- const tgt = page.id;
44
- for (const src of page.inbound_links ?? []) {
45
- if (src === tgt)
46
- continue;
47
- if (!nodeIds.has(src)) {
48
- dangling.add(src);
49
- continue;
50
- }
51
- const key = `${src}${SEP}${tgt}`;
52
- const existing = edgeMap.get(key);
53
- if (existing) {
54
- if (existing.kind === "outbound")
55
- existing.kind = "both";
56
- // already 'inbound' or 'both' → keep
57
- }
58
- else {
59
- edgeMap.set(key, { source: src, target: tgt, kind: "inbound" });
60
- }
61
- }
62
- }
63
- const edges = Array.from(edgeMap.values());
64
- // Compute resolved in/out degree from final edge list.
65
- const inDeg = new Map();
66
- const outDeg = new Map();
67
- for (const e of edges) {
68
- outDeg.set(e.source, (outDeg.get(e.source) ?? 0) + 1);
69
- inDeg.set(e.target, (inDeg.get(e.target) ?? 0) + 1);
70
- }
71
- const nodes = pages.map((p) => {
72
- const out_degree = outDeg.get(p.id) ?? 0;
73
- const in_degree = inDeg.get(p.id) ?? 0;
74
- return {
75
- id: p.id,
76
- title: p.title,
77
- category: p.category,
78
- degree: in_degree + out_degree,
79
- in_degree,
80
- out_degree,
81
- stale: p.stale,
82
- updated_at: p.updated_at,
83
- };
84
- });
85
- const orphans = nodes.filter((n) => n.in_degree === 0).length;
86
- const isolated = nodes.filter((n) => n.degree === 0).length;
87
- return {
88
- nodes,
89
- edges,
90
- meta: {
91
- total_pages: nodes.length,
92
- total_edges: edges.length,
93
- orphans,
94
- isolated,
95
- dangling_refs: Array.from(dangling).sort(),
96
- generated_at: generatedAt,
97
- },
98
- };
99
- }
100
- catch (e) {
101
- return {
102
- nodes: [],
103
- edges: [],
104
- meta: {
105
- total_pages: 0, total_edges: 0, orphans: 0, isolated: 0,
106
- dangling_refs: [], generated_at: generatedAt,
107
- },
108
- error: e?.message ?? String(e),
109
- };
110
- }
111
- }
@@ -1,162 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateDependencyGraph = generateDependencyGraph;
7
- const node_path_1 = __importDefault(require("node:path"));
8
- const list_modules_1 = require("./list-modules");
9
- async function generateDependencyGraph(input) {
10
- try {
11
- const projectPath = node_path_1.default.resolve(input.project_path);
12
- // Scan all modules
13
- const listResult = await (0, list_modules_1.listModules)({
14
- path: projectPath,
15
- extensions: input.extensions,
16
- });
17
- const modules = listResult.modules ?? [];
18
- if (modules.length === 0) {
19
- return {
20
- nodes: [],
21
- edges: [],
22
- shared_tables: {},
23
- shared_endpoints: {},
24
- most_connected: [],
25
- summary: "No source files found in the project.",
26
- };
27
- }
28
- // Build id from relative path (normalised for use as node id)
29
- const relPath = (filePath) => node_path_1.default.relative(projectPath, filePath).replace(/\\/g, "/");
30
- // Build nodes map
31
- const nodeMap = new Map();
32
- for (const mod of modules) {
33
- const id = relPath(mod.path);
34
- nodeMap.set(id, {
35
- id,
36
- label: node_path_1.default.basename(mod.path),
37
- language: mod.language,
38
- classes: mod.classes.length,
39
- functions: mod.functions.length,
40
- db_tables: mod.db_tables,
41
- endpoints: mod.endpoints,
42
- });
43
- }
44
- // Build import edges: for each module's dependency list, look for matches in node IDs
45
- const edges = [];
46
- const edgeSet = new Set(); // avoid duplicates
47
- for (const mod of modules) {
48
- const fromId = relPath(mod.path);
49
- for (const dep of mod.dependencies) {
50
- // Try to resolve dep to a known node (relative path match or basename match)
51
- for (const [nodeId] of nodeMap) {
52
- // Match if the dependency string is contained in the node path
53
- // (e.g., dep = "./user-service" matches "src/user-service.ts")
54
- const depNorm = dep.replace(/^[./]+/, "").replace(/\\/g, "/").toLowerCase();
55
- const nodeNorm = nodeId.replace(/\\/g, "/").toLowerCase();
56
- if (depNorm.length > 2 &&
57
- (nodeNorm.includes(depNorm) || nodeNorm.replace(/\.\w+$/, "").endsWith(depNorm))) {
58
- const key = `${fromId}->${nodeId}`;
59
- if (fromId !== nodeId && !edgeSet.has(key)) {
60
- edgeSet.add(key);
61
- edges.push({ from: fromId, to: nodeId, type: "imports" });
62
- }
63
- }
64
- }
65
- }
66
- }
67
- // Shared DB tables
68
- const tableToModules = new Map();
69
- for (const mod of modules) {
70
- for (const table of mod.db_tables) {
71
- const key = table.toLowerCase();
72
- if (!tableToModules.has(key))
73
- tableToModules.set(key, []);
74
- tableToModules.get(key).push(relPath(mod.path));
75
- }
76
- }
77
- const shared_tables = {};
78
- for (const [table, mods] of tableToModules) {
79
- if (mods.length > 1) {
80
- shared_tables[table] = mods;
81
- // Add shared_table edges
82
- for (let i = 0; i < mods.length; i++) {
83
- for (let j = i + 1; j < mods.length; j++) {
84
- const key = `${mods[i]}<>${mods[j]}:${table}`;
85
- if (!edgeSet.has(key)) {
86
- edgeSet.add(key);
87
- edges.push({ from: mods[i], to: mods[j], type: "shared_table" });
88
- }
89
- }
90
- }
91
- }
92
- }
93
- // Shared endpoints
94
- const endpointToModules = new Map();
95
- for (const mod of modules) {
96
- for (const ep of mod.endpoints) {
97
- if (!endpointToModules.has(ep))
98
- endpointToModules.set(ep, []);
99
- endpointToModules.get(ep).push(relPath(mod.path));
100
- }
101
- }
102
- const shared_endpoints = {};
103
- for (const [ep, mods] of endpointToModules) {
104
- if (mods.length > 1)
105
- shared_endpoints[ep] = mods;
106
- }
107
- // Most connected nodes (by total edge count)
108
- const connectionCount = new Map();
109
- for (const edge of edges) {
110
- connectionCount.set(edge.from, (connectionCount.get(edge.from) ?? 0) + 1);
111
- connectionCount.set(edge.to, (connectionCount.get(edge.to) ?? 0) + 1);
112
- }
113
- const most_connected = Array.from(connectionCount.entries())
114
- .map(([id, count]) => ({
115
- id,
116
- label: nodeMap.get(id)?.label ?? id,
117
- connection_count: count,
118
- }))
119
- .sort((a, b) => b.connection_count - a.connection_count)
120
- .slice(0, 10);
121
- // Apply focus filter: if focus is set, only include nodes/edges reachable from it
122
- let nodes = Array.from(nodeMap.values());
123
- let filteredEdges = edges;
124
- if (input.focus) {
125
- const focusNorm = input.focus.toLowerCase();
126
- const focusId = nodes.find((n) => n.id.toLowerCase().includes(focusNorm))?.id;
127
- if (focusId) {
128
- const reachable = new Set([focusId]);
129
- // BFS: include direct neighbours
130
- for (const e of edges) {
131
- if (e.from === focusId)
132
- reachable.add(e.to);
133
- if (e.to === focusId)
134
- reachable.add(e.from);
135
- }
136
- nodes = nodes.filter((n) => reachable.has(n.id));
137
- filteredEdges = edges.filter((e) => reachable.has(e.from) && reachable.has(e.to));
138
- }
139
- }
140
- const sharedTableCount = Object.keys(shared_tables).length;
141
- const summary = [
142
- `${nodes.length} modules, ${filteredEdges.length} dependencies`,
143
- sharedTableCount > 0 ? `${sharedTableCount} shared DB table(s)` : null,
144
- most_connected.length > 0
145
- ? `Most connected: ${most_connected[0].label} (${most_connected[0].connection_count} links)`
146
- : null,
147
- ]
148
- .filter(Boolean)
149
- .join(" · ");
150
- return {
151
- nodes,
152
- edges: filteredEdges,
153
- shared_tables,
154
- shared_endpoints,
155
- most_connected,
156
- summary,
157
- };
158
- }
159
- catch (e) {
160
- return { error: e?.message ?? String(e) };
161
- }
162
- }
@@ -1,213 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getSchemataGuidance = getSchemataGuidance;
7
- const node_path_1 = __importDefault(require("node:path"));
8
- const promises_1 = __importDefault(require("node:fs/promises"));
9
- /**
10
- * get_schema_guidance
11
- *
12
- * Analyzes what documents should exist based on the project schema and current wiki state.
13
- * Helps users understand what to create next.
14
- *
15
- * Input:
16
- * - project_path: string
17
- * - domain?: string (optional; auto-detected from schema if not provided)
18
- *
19
- * Output:
20
- * - domain: detected domain
21
- * - recommended_pages: list of pages that should exist with reasons
22
- * - existing_pages: what's already in the wiki
23
- * - missing_pages: high-priority missing documents
24
- * - recommendations: actionable next steps
25
- */
26
- async function getSchemataGuidance(input) {
27
- const projectPath = node_path_1.default.resolve(input.project_path);
28
- const docuDir = node_path_1.default.join(projectPath, ".docuflow");
29
- const wikiDir = node_path_1.default.join(docuDir, "wiki");
30
- const schemaPath = node_path_1.default.join(docuDir, "schema.md");
31
- // Read schema to understand domain
32
- let domain = input.domain || "General";
33
- try {
34
- const schemaContent = await promises_1.default.readFile(schemaPath, "utf-8");
35
- if (schemaContent.includes("Research"))
36
- domain = "Research";
37
- else if (schemaContent.includes("Business"))
38
- domain = "Business";
39
- else if (schemaContent.includes("Architecture"))
40
- domain = "Code/Architecture";
41
- else if (schemaContent.includes("Personal"))
42
- domain = "Personal";
43
- }
44
- catch {
45
- // Use default if schema not readable
46
- }
47
- // Scan wiki for existing pages
48
- const indexPath = node_path_1.default.join(docuDir, "index.md");
49
- const indexContent = await promises_1.default
50
- .readFile(indexPath, "utf-8")
51
- .catch(() => "");
52
- // Count pages by category
53
- const existingPages = [];
54
- const categories = ["entities", "concepts", "syntheses", "timelines"];
55
- for (const cat of categories) {
56
- const catDir = node_path_1.default.join(wikiDir, cat);
57
- try {
58
- const files = await promises_1.default.readdir(catDir);
59
- for (const file of files.filter((f) => f.endsWith(".md"))) {
60
- existingPages.push({
61
- name: file.replace(".md", ""),
62
- category: cat,
63
- });
64
- }
65
- }
66
- catch {
67
- // Directory may not exist
68
- }
69
- }
70
- // Define recommended pages by domain
71
- const recommendedByDomain = {
72
- "Code/Architecture": [
73
- {
74
- name: "architecture_overview",
75
- category: "syntheses",
76
- suggested_title: "System Architecture Overview",
77
- reason: "High-level view of how all components fit together",
78
- },
79
- {
80
- name: "core_patterns",
81
- category: "concepts",
82
- suggested_title: "Core Architectural Patterns",
83
- reason: "Design patterns used throughout the codebase",
84
- },
85
- {
86
- name: "module_dependencies",
87
- category: "concepts",
88
- suggested_title: "Module Dependencies",
89
- reason: "How modules depend on and integrate with each other",
90
- },
91
- {
92
- name: "data_flow",
93
- category: "syntheses",
94
- suggested_title: "Data Flow Diagram",
95
- reason: "How data moves through the system",
96
- },
97
- ],
98
- Research: [
99
- {
100
- name: "research_overview",
101
- category: "syntheses",
102
- suggested_title: "Research Domain Overview",
103
- reason: "Big picture of the research area",
104
- },
105
- {
106
- name: "key_findings",
107
- category: "syntheses",
108
- suggested_title: "Key Findings & Synthesis",
109
- reason: "Major discoveries and insights",
110
- },
111
- {
112
- name: "contradictions",
113
- category: "syntheses",
114
- suggested_title: "Areas of Contradiction",
115
- reason: "Where researchers disagree",
116
- },
117
- {
118
- name: "open_questions",
119
- category: "concepts",
120
- suggested_title: "Open Research Questions",
121
- reason: "What's still unknown in this domain",
122
- },
123
- ],
124
- Business: [
125
- {
126
- name: "market_overview",
127
- category: "syntheses",
128
- suggested_title: "Market Overview",
129
- reason: "Big picture of the market and competitive landscape",
130
- },
131
- {
132
- name: "competitive_analysis",
133
- category: "syntheses",
134
- suggested_title: "Competitive Analysis",
135
- reason: "Comparison of key competitors",
136
- },
137
- {
138
- name: "opportunities",
139
- category: "syntheses",
140
- suggested_title: "Market Opportunities",
141
- reason: "Gaps and growth areas",
142
- },
143
- {
144
- name: "risks",
145
- category: "concepts",
146
- suggested_title: "Market Risks",
147
- reason: "Threats and challenges",
148
- },
149
- ],
150
- Personal: [
151
- {
152
- name: "learning_goals",
153
- category: "concepts",
154
- suggested_title: "Learning Goals",
155
- reason: "What you want to learn in this domain",
156
- },
157
- {
158
- name: "key_insights",
159
- category: "syntheses",
160
- suggested_title: "Key Personal Insights",
161
- reason: "Major learnings and takeaways",
162
- },
163
- {
164
- name: "action_items",
165
- category: "concepts",
166
- suggested_title: "Action Items & Next Steps",
167
- reason: "What to do with this knowledge",
168
- },
169
- {
170
- name: "resources",
171
- category: "concepts",
172
- suggested_title: "Key Resources",
173
- reason: "Important links, books, people",
174
- },
175
- ],
176
- };
177
- const recommended = recommendedByDomain[domain] || recommendedByDomain.General;
178
- // Find missing pages
179
- const missingPages = [];
180
- for (const rec of recommended) {
181
- const found = existingPages.find((p) => p.name === rec.name);
182
- if (!found) {
183
- missingPages.push(rec.suggested_title);
184
- }
185
- }
186
- // Generate recommendations
187
- const recommendations = [];
188
- if (existingPages.length === 0) {
189
- recommendations.push("🌱 Start by ingesting your first source");
190
- recommendations.push("📝 Create an overview/synthesis page");
191
- }
192
- else if (existingPages.length < 10) {
193
- recommendations.push("📚 Add more sources to deepen understanding");
194
- if (missingPages.length > 0) {
195
- recommendations.push(`⚠️ Consider creating: ${missingPages[0]}`);
196
- }
197
- }
198
- else if (existingPages.length > 50) {
199
- recommendations.push("✅ You have a solid wiki foundation");
200
- recommendations.push("🔍 Run lint_wiki to health-check for gaps");
201
- recommendations.push("🔗 Verify cross-references are accurate");
202
- }
203
- if (missingPages.length > 2) {
204
- recommendations.push(`💡 Biggest gap: ${missingPages[0]} could improve wiki significantly`);
205
- }
206
- return {
207
- domain,
208
- recommended_pages: recommended,
209
- existing_pages: existingPages,
210
- missing_pages: missingPages,
211
- recommendations,
212
- };
213
- }