@doquflow/cli 0.2.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/dist/commands/init-interactive.js +346 -0
- package/dist/commands/init.js +165 -5
- package/dist/commands/status.js +88 -10
- package/dist/commands/suggest.js +130 -0
- package/dist/index.js +15 -5
- package/package.json +2 -2
|
@@ -0,0 +1,346 @@
|
|
|
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.runInteractive = runInteractive;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
11
|
+
const init_1 = require("./init");
|
|
12
|
+
async function prompt(question) {
|
|
13
|
+
const rl = node_readline_1.default.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout,
|
|
16
|
+
});
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
resolve(answer);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function selectDomain() {
|
|
25
|
+
console.log("\nš What domain is your wiki for?");
|
|
26
|
+
console.log(" 1) Code & Architecture");
|
|
27
|
+
console.log(" 2) Research & Analysis");
|
|
28
|
+
console.log(" 3) Business & Markets");
|
|
29
|
+
console.log(" 4) Personal Knowledge");
|
|
30
|
+
let selection = "";
|
|
31
|
+
while (!["1", "2", "3", "4"].includes(selection)) {
|
|
32
|
+
selection = await prompt("Select (1-4): ");
|
|
33
|
+
}
|
|
34
|
+
const domains = {
|
|
35
|
+
"1": "code",
|
|
36
|
+
"2": "research",
|
|
37
|
+
"3": "business",
|
|
38
|
+
"4": "personal",
|
|
39
|
+
};
|
|
40
|
+
return domains[selection];
|
|
41
|
+
}
|
|
42
|
+
async function getProjectInfo(domain) {
|
|
43
|
+
console.log(`\nš Tell me about your ${domain} wiki`);
|
|
44
|
+
const name = await prompt("Project name (e.g., 'MyProject', 'Research2026'): ");
|
|
45
|
+
const description = await prompt("Brief description (what will this wiki track?): ");
|
|
46
|
+
return { name, description };
|
|
47
|
+
}
|
|
48
|
+
function getSchemaForDomain(domain) {
|
|
49
|
+
const schemas = {
|
|
50
|
+
code: `# Docuflow Wiki Schema - Code/Architecture
|
|
51
|
+
|
|
52
|
+
## Domain
|
|
53
|
+
Architecture and codebase documentation.
|
|
54
|
+
|
|
55
|
+
## Wiki Structure
|
|
56
|
+
|
|
57
|
+
### Entities (entities/)
|
|
58
|
+
- Services: key microservices and components
|
|
59
|
+
- APIs: public interfaces and endpoints
|
|
60
|
+
- Databases: data models and schemas
|
|
61
|
+
- Frameworks: libraries and tools used
|
|
62
|
+
|
|
63
|
+
### Concepts (concepts/)
|
|
64
|
+
- Design Patterns: architectural and coding patterns
|
|
65
|
+
- Principles: design principles and guidelines
|
|
66
|
+
- Integrations: how components work together
|
|
67
|
+
- Configuration: important settings and options
|
|
68
|
+
|
|
69
|
+
### Syntheses (syntheses/)
|
|
70
|
+
- Architecture Overview: system design
|
|
71
|
+
- Decision Records: design decisions
|
|
72
|
+
- Deployment Guide: how to deploy
|
|
73
|
+
- API Reference: complete API docs
|
|
74
|
+
|
|
75
|
+
### Timelines (timelines/)
|
|
76
|
+
- Version History: evolution of the system
|
|
77
|
+
- Roadmap: planned changes
|
|
78
|
+
|
|
79
|
+
## Cross-Reference Patterns
|
|
80
|
+
- "integrates with" - components/services that work together
|
|
81
|
+
- "implemented by" - which entities implement a concept
|
|
82
|
+
- "uses pattern" - which architecture patterns apply
|
|
83
|
+
- "depends on" - dependencies between components
|
|
84
|
+
|
|
85
|
+
## Metadata
|
|
86
|
+
Each page should include:
|
|
87
|
+
- created_at: when first documented
|
|
88
|
+
- updated_at: last update date
|
|
89
|
+
- tech_stack: relevant technologies
|
|
90
|
+
- contributors: who wrote/contributed
|
|
91
|
+
`,
|
|
92
|
+
research: `# Docuflow Wiki Schema - Research
|
|
93
|
+
|
|
94
|
+
## Domain
|
|
95
|
+
Research findings and analysis.
|
|
96
|
+
|
|
97
|
+
## Wiki Structure
|
|
98
|
+
|
|
99
|
+
### Entities (entities/)
|
|
100
|
+
- Papers: academic papers and articles
|
|
101
|
+
- Researchers: key people in the field
|
|
102
|
+
- Conferences: important venues
|
|
103
|
+
- Datasets: data sources used
|
|
104
|
+
|
|
105
|
+
### Concepts (concepts/)
|
|
106
|
+
- Methodologies: research methods
|
|
107
|
+
- Theories: key theories and frameworks
|
|
108
|
+
- Open Problems: unsolved questions
|
|
109
|
+
- Keywords: important terms
|
|
110
|
+
|
|
111
|
+
### Syntheses (syntheses/)
|
|
112
|
+
- Literature Review: synthesis of papers
|
|
113
|
+
- Findings: key discoveries
|
|
114
|
+
- Future Work: research directions
|
|
115
|
+
- Contradictions: areas of disagreement
|
|
116
|
+
|
|
117
|
+
### Timelines (timelines/)
|
|
118
|
+
- Research Evolution: how field evolved
|
|
119
|
+
- Timeline of Discoveries: key milestones
|
|
120
|
+
|
|
121
|
+
## Cross-Reference Patterns
|
|
122
|
+
- "cites" - references between papers
|
|
123
|
+
- "extends" - builds on prior work
|
|
124
|
+
- "contradicts" - disagreement between sources
|
|
125
|
+
- "validates" - experimental confirmation
|
|
126
|
+
- "relates to" - topical connection
|
|
127
|
+
|
|
128
|
+
## Metadata
|
|
129
|
+
Each page should include:
|
|
130
|
+
- created_at: when added
|
|
131
|
+
- updated_at: last refresh
|
|
132
|
+
- sources: which papers/sources
|
|
133
|
+
- citations: number of times cited
|
|
134
|
+
- confidence: how confident in findings
|
|
135
|
+
`,
|
|
136
|
+
business: `# Docuflow Wiki Schema - Business
|
|
137
|
+
|
|
138
|
+
## Domain
|
|
139
|
+
Business and competitive analysis.
|
|
140
|
+
|
|
141
|
+
## Wiki Structure
|
|
142
|
+
|
|
143
|
+
### Entities (entities/)
|
|
144
|
+
- Companies: competitors and partners
|
|
145
|
+
- Products: key offerings
|
|
146
|
+
- Markets: market segments
|
|
147
|
+
- Customers: customer types
|
|
148
|
+
|
|
149
|
+
### Concepts (concepts/)
|
|
150
|
+
- Business Models: how companies make money
|
|
151
|
+
- Market Segments: addressable markets
|
|
152
|
+
- Capabilities: key competitive advantages
|
|
153
|
+
- Trends: market trends
|
|
154
|
+
|
|
155
|
+
### Syntheses (syntheses/)
|
|
156
|
+
- Competitive Analysis: comparison matrix
|
|
157
|
+
- Market Overview: market positioning
|
|
158
|
+
- Opportunities: growth opportunities
|
|
159
|
+
- Risks: market risks
|
|
160
|
+
|
|
161
|
+
### Timelines (timelines/)
|
|
162
|
+
- Market Evolution: how market changed
|
|
163
|
+
- Competitive Timeline: competitor moves
|
|
164
|
+
|
|
165
|
+
## Cross-Reference Patterns
|
|
166
|
+
- "competes with" - direct competitors
|
|
167
|
+
- "targets" - goes after customer/market
|
|
168
|
+
- "partners with" - partnerships
|
|
169
|
+
- "disrupts" - disruption threat
|
|
170
|
+
- "complements" - complementary products
|
|
171
|
+
|
|
172
|
+
## Metadata
|
|
173
|
+
Each page should include:
|
|
174
|
+
- created_at: when first documented
|
|
175
|
+
- updated_at: last update
|
|
176
|
+
- market_size: TAM/SAM/SOM if known
|
|
177
|
+
- growth_rate: annual growth
|
|
178
|
+
- sources: where info came from
|
|
179
|
+
`,
|
|
180
|
+
personal: `# Docuflow Wiki Schema - Personal
|
|
181
|
+
|
|
182
|
+
## Domain
|
|
183
|
+
Personal knowledge, learning, and goals.
|
|
184
|
+
|
|
185
|
+
## Wiki Structure
|
|
186
|
+
|
|
187
|
+
### Entities (entities/)
|
|
188
|
+
- Topics: areas of interest/expertise
|
|
189
|
+
- Resources: books, courses, websites
|
|
190
|
+
- People: mentors, collaborators
|
|
191
|
+
- Projects: personal projects
|
|
192
|
+
|
|
193
|
+
### Concepts (concepts/)
|
|
194
|
+
- Learning Goals: what to learn
|
|
195
|
+
- Skills: competencies to develop
|
|
196
|
+
- Insights: key personal learnings
|
|
197
|
+
- Practices: habits and routines
|
|
198
|
+
|
|
199
|
+
### Syntheses (syntheses/)
|
|
200
|
+
- Reflections: deeper thinking
|
|
201
|
+
- Progress Updates: tracking learning
|
|
202
|
+
- Connections: how ideas relate
|
|
203
|
+
- Action Plans: what to do next
|
|
204
|
+
|
|
205
|
+
### Timelines (timelines/)
|
|
206
|
+
- Learning Journey: personal evolution
|
|
207
|
+
- Milestones: key achievements
|
|
208
|
+
|
|
209
|
+
## Cross-Reference Patterns
|
|
210
|
+
- "builds on" - prerequisite knowledge
|
|
211
|
+
- "connects to" - related topics
|
|
212
|
+
- "informs" - influences thinking
|
|
213
|
+
- "inspires" - inspiration source
|
|
214
|
+
- "applies to" - practical application
|
|
215
|
+
|
|
216
|
+
## Metadata
|
|
217
|
+
Each page should include:
|
|
218
|
+
- created_at: when started learning
|
|
219
|
+
- updated_at: last review
|
|
220
|
+
- relevance: importance (high/medium/low)
|
|
221
|
+
- mastery_level: expertise level (beginner/intermediate/expert)
|
|
222
|
+
- time_invested: hours spent
|
|
223
|
+
`,
|
|
224
|
+
};
|
|
225
|
+
return schemas[domain];
|
|
226
|
+
}
|
|
227
|
+
function getPlanningTemplate(domain, projectName) {
|
|
228
|
+
return `# ${projectName} Wiki Plan
|
|
229
|
+
|
|
230
|
+
## Goal
|
|
231
|
+
Document and organize knowledge for ${projectName}.
|
|
232
|
+
|
|
233
|
+
## Initial Sources to Add
|
|
234
|
+
1. [ ] README or main overview
|
|
235
|
+
2. [ ] Key source file 1
|
|
236
|
+
3. [ ] Key source file 2
|
|
237
|
+
|
|
238
|
+
## Key Entities to Define
|
|
239
|
+
- Entity 1: [description]
|
|
240
|
+
- Entity 2: [description]
|
|
241
|
+
|
|
242
|
+
## Key Concepts to Extract
|
|
243
|
+
- Concept 1: [description]
|
|
244
|
+
- Concept 2: [description]
|
|
245
|
+
|
|
246
|
+
## First Questions to Answer
|
|
247
|
+
1. [What do you want to understand first?]
|
|
248
|
+
2. [What relationships are important?]
|
|
249
|
+
|
|
250
|
+
## Success Criteria
|
|
251
|
+
- [ ] Successfully ingested first 3 sources
|
|
252
|
+
- [ ] Created 10+ wiki pages
|
|
253
|
+
- [ ] Can answer key questions
|
|
254
|
+
- [ ] Wiki is at 80%+ health score
|
|
255
|
+
|
|
256
|
+
## Next Review Date
|
|
257
|
+
[Date for first maintenance check]
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
async function runInteractive() {
|
|
261
|
+
console.log("\nš Welcome to Docuflow Wiki Setup!\n");
|
|
262
|
+
console.log("I'll help you set up a wiki for your project.");
|
|
263
|
+
console.log("This should take about 2 minutes. You can customize later.\n");
|
|
264
|
+
// Get domain
|
|
265
|
+
const domain = await selectDomain();
|
|
266
|
+
console.log(`ā Selected: ${domain}`);
|
|
267
|
+
// Get project info
|
|
268
|
+
const { name: projectName, description } = await getProjectInfo(domain);
|
|
269
|
+
console.log(`ā Project: "${projectName}" - ${description}`);
|
|
270
|
+
// Confirm setup
|
|
271
|
+
console.log("\n⨠I'll create a wiki structure for you with recommended schema.");
|
|
272
|
+
const confirm = await prompt("Ready to proceed? (y/n): ");
|
|
273
|
+
if (confirm.toLowerCase() !== "y") {
|
|
274
|
+
console.log("\nš Setup cancelled.");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
// Get schema for domain
|
|
278
|
+
const domainSchema = getSchemaForDomain(domain);
|
|
279
|
+
const planTemplate = getPlanningTemplate(domain, projectName);
|
|
280
|
+
console.log("\nš Creating wiki structure...");
|
|
281
|
+
const docuDir = node_path_1.default.join(process.cwd(), ".docuflow");
|
|
282
|
+
const wikiDir = node_path_1.default.join(docuDir, "wiki");
|
|
283
|
+
// Create directories
|
|
284
|
+
await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "entities"), { recursive: true });
|
|
285
|
+
await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "concepts"), { recursive: true });
|
|
286
|
+
await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "syntheses"), { recursive: true });
|
|
287
|
+
await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "timelines"), { recursive: true });
|
|
288
|
+
await promises_1.default.mkdir(node_path_1.default.join(docuDir, "sources"), { recursive: true });
|
|
289
|
+
await promises_1.default.mkdir(node_path_1.default.join(docuDir, "specs"), { recursive: true });
|
|
290
|
+
console.log(" ā Created wiki directories");
|
|
291
|
+
// Write schema with domain-specific content
|
|
292
|
+
await promises_1.default.writeFile(node_path_1.default.join(docuDir, "schema.md"), domainSchema, "utf8");
|
|
293
|
+
console.log(" ā Created schema.md with domain-specific structure");
|
|
294
|
+
// Write index and log
|
|
295
|
+
const indexContent = `# Wiki Index
|
|
296
|
+
|
|
297
|
+
Auto-maintained catalog of all wiki pages.
|
|
298
|
+
|
|
299
|
+
## Generated: ${new Date().toISOString()}
|
|
300
|
+
|
|
301
|
+
(Index is automatically updated as you ingest sources)
|
|
302
|
+
`;
|
|
303
|
+
await promises_1.default.writeFile(node_path_1.default.join(docuDir, "index.md"), indexContent, "utf8");
|
|
304
|
+
console.log(" ā Created index.md");
|
|
305
|
+
const logContent = `# Operation Log
|
|
306
|
+
|
|
307
|
+
Record of all wiki operations.
|
|
308
|
+
|
|
309
|
+
## [${new Date().toISOString()}] init | Wiki initialized
|
|
310
|
+
- Domain: ${domain}
|
|
311
|
+
- Project: ${projectName}
|
|
312
|
+
- Description: ${description}
|
|
313
|
+
`;
|
|
314
|
+
await promises_1.default.writeFile(node_path_1.default.join(docuDir, "log.md"), logContent, "utf8");
|
|
315
|
+
console.log(" ā Created log.md");
|
|
316
|
+
// Write planning template
|
|
317
|
+
const planPath = node_path_1.default.join(docuDir, "PLAN.md");
|
|
318
|
+
await promises_1.default.writeFile(planPath, planTemplate, "utf8");
|
|
319
|
+
console.log(" ā Created PLAN.md (interactive planning guide)");
|
|
320
|
+
// Generate CLAUDE.md so Claude Code picks up DocuFlow automatically
|
|
321
|
+
const claudeMdContent = (0, init_1.buildClaudeMd)(process.cwd());
|
|
322
|
+
const claudeMdPath = node_path_1.default.join(process.cwd(), "CLAUDE.md");
|
|
323
|
+
if (node_fs_1.default.existsSync(claudeMdPath)) {
|
|
324
|
+
const existing = await promises_1.default.readFile(claudeMdPath, "utf8");
|
|
325
|
+
if (existing.includes("DocuFlow")) {
|
|
326
|
+
const withoutDocuflow = existing.replace(/\n?# DocuFlow[\s\S]*/, "").trimEnd();
|
|
327
|
+
await promises_1.default.writeFile(claudeMdPath, withoutDocuflow + "\n\n" + claudeMdContent, "utf8");
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
await promises_1.default.appendFile(claudeMdPath, "\n\n" + claudeMdContent, "utf8");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
await promises_1.default.writeFile(claudeMdPath, claudeMdContent, "utf8");
|
|
335
|
+
}
|
|
336
|
+
console.log(" ā Created CLAUDE.md (Claude Code will read DocuFlow tool instructions automatically)");
|
|
337
|
+
console.log("\nā
Wiki successfully initialized!\n");
|
|
338
|
+
// Print summary and next steps
|
|
339
|
+
console.log("š Next Steps:");
|
|
340
|
+
console.log(" 1. Review your schema: .docuflow/schema.md");
|
|
341
|
+
console.log(" 2. Review your plan: .docuflow/PLAN.md");
|
|
342
|
+
console.log(" 3. Add first source: copy to .docuflow/sources/");
|
|
343
|
+
console.log(" 4. Ask Claude: 'Ingest README.md into my wiki'");
|
|
344
|
+
console.log(" 5. Ask Claude: 'What should my wiki contain?'");
|
|
345
|
+
console.log("\nš” Tip: Claude Code will automatically read CLAUDE.md for DocuFlow instructions.\n");
|
|
346
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildClaudeMd = buildClaudeMd;
|
|
6
7
|
exports.run = run;
|
|
7
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
9
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
@@ -28,6 +29,125 @@ function resolveServerBin() {
|
|
|
28
29
|
return node_path_1.default.resolve(__dirname, "..", "..", "server", "dist", "index.js");
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
async function copyTemplateFile(templateName, destPath) {
|
|
33
|
+
try {
|
|
34
|
+
// Try package-installed location first
|
|
35
|
+
const templatePath = require.resolve(`@doquflow/cli/templates/${templateName}`);
|
|
36
|
+
const content = await promises_1.default.readFile(templatePath, "utf8");
|
|
37
|
+
await promises_1.default.writeFile(destPath, content, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
try {
|
|
41
|
+
// Fallback: monorepo sibling path (dev environment)
|
|
42
|
+
const templatePath = node_path_1.default.resolve(__dirname, "..", "..", "templates", templateName);
|
|
43
|
+
const content = await promises_1.default.readFile(templatePath, "utf8");
|
|
44
|
+
await promises_1.default.writeFile(destPath, content, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
// If template not found, create a minimal version
|
|
48
|
+
console.warn(` ā Could not find template for ${templateName}, creating minimal version`);
|
|
49
|
+
if (templateName === "schema.md") {
|
|
50
|
+
await promises_1.default.writeFile(destPath, "# Docuflow Wiki Schema\n\n## Domain\n[Edit this file to customize your wiki]\n", "utf8");
|
|
51
|
+
}
|
|
52
|
+
else if (templateName === "index.md") {
|
|
53
|
+
await promises_1.default.writeFile(destPath, "# Wiki Index\n\nAuto-maintained catalog of pages.\n", "utf8");
|
|
54
|
+
}
|
|
55
|
+
else if (templateName === "log.md") {
|
|
56
|
+
await promises_1.default.writeFile(destPath, "# Operation Log\n\nRecord of wiki operations.\n", "utf8");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function buildClaudeMd(projectDir) {
|
|
62
|
+
return `# DocuFlow ā AI Documentation Assistant
|
|
63
|
+
|
|
64
|
+
DocuFlow is an MCP server that gives you structured access to this codebase and maintains a living wiki.
|
|
65
|
+
It is registered in your Claude Desktop config and available as MCP tools in every session.
|
|
66
|
+
|
|
67
|
+
## Codebase Scanner Tools
|
|
68
|
+
|
|
69
|
+
- **read_module** ā Analyse a single source file. Returns language, classes, functions, dependencies, DB tables, endpoints, config refs, and raw content (first 8 KB).
|
|
70
|
+
- Example: \`read_module({ path: "src/UserService.cs" })\`
|
|
71
|
+
- **list_modules** ā Walk a directory and extract facts for every non-binary file. Use this to understand the full project in one call.
|
|
72
|
+
- Example: \`list_modules({ path: "${projectDir}" })\`
|
|
73
|
+
- **write_spec** ā Persist a markdown spec to \`.docuflow/specs/<filename>.md\` and update the index.
|
|
74
|
+
- Example: \`write_spec({ project_path: "${projectDir}", filename: "UserService", content: "# UserService\\n..." })\`
|
|
75
|
+
- **read_specs** ā Read previously written specs, optionally filtered by name.
|
|
76
|
+
- Example: \`read_specs({ project_path: "${projectDir}" })\`
|
|
77
|
+
|
|
78
|
+
## Wiki Pipeline Tools
|
|
79
|
+
|
|
80
|
+
- **ingest_source** ā Ingest a markdown file from \`.docuflow/sources/\` and generate wiki pages (entities, concepts).
|
|
81
|
+
- **update_index** ā Rebuild \`.docuflow/index.md\` from all wiki pages.
|
|
82
|
+
- **list_wiki** ā List all wiki pages, optionally filtered by category (entity/concept/timeline/synthesis).
|
|
83
|
+
- **wiki_search** ā BM25 search across all wiki pages. Returns ranked results with previews.
|
|
84
|
+
- **query_wiki** ā One-stop Q&A: searches wiki, synthesises an answer, returns source citations.
|
|
85
|
+
- **synthesize_answer** ā Generate a markdown synthesis from a list of specific wiki page IDs.
|
|
86
|
+
- **save_answer_as_page** ā Persist a synthesised answer back into the wiki (knowledge compounding).
|
|
87
|
+
|
|
88
|
+
## Health & Guidance Tools
|
|
89
|
+
|
|
90
|
+
- **lint_wiki** ā Health check: orphan pages, broken refs, stale content, metadata gaps. Returns a 0ā100 health score.
|
|
91
|
+
- **get_schema_guidance** ā Analyse what wiki pages should exist based on the schema and current state.
|
|
92
|
+
- **preview_generation** ā Preview what a tool will do before running it.
|
|
93
|
+
|
|
94
|
+
## Common Workflows
|
|
95
|
+
|
|
96
|
+
### First time ā understand the codebase
|
|
97
|
+
\`\`\`
|
|
98
|
+
list_modules({ path: "${projectDir}" })
|
|
99
|
+
ā read the language breakdown and dependency map
|
|
100
|
+
ā write_spec each important module
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
### Ongoing ā answer a question
|
|
104
|
+
\`\`\`
|
|
105
|
+
query_wiki({ project_path: "${projectDir}", question: "How does authentication work?" })
|
|
106
|
+
ā save_answer_as_page if the answer is worth keeping
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
### Maintenance ā check wiki health
|
|
110
|
+
\`\`\`
|
|
111
|
+
lint_wiki({ project_path: "${projectDir}" })
|
|
112
|
+
ā fix orphans and broken refs
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
## Storage Layout
|
|
116
|
+
|
|
117
|
+
\`\`\`
|
|
118
|
+
.docuflow/
|
|
119
|
+
āāā specs/ Legacy spec files written by write_spec
|
|
120
|
+
āāā wiki/ LLM-generated wiki pages
|
|
121
|
+
ā āāā entities/ Named things (services, APIs, databases)
|
|
122
|
+
ā āāā concepts/ Design patterns, principles, integrations
|
|
123
|
+
ā āāā timelines/ Chronological pages
|
|
124
|
+
ā āāā syntheses/ Cross-cutting synthesis pages
|
|
125
|
+
āāā sources/ Raw input files for ingest_source
|
|
126
|
+
āāā schema.md Wiki configuration (edit to customise)
|
|
127
|
+
āāā index.md Auto-maintained catalog
|
|
128
|
+
āāā log.md Operation log
|
|
129
|
+
\`\`\`
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
async function writeClaudeMd(projectDir) {
|
|
133
|
+
const claudeMdPath = node_path_1.default.join(projectDir, "CLAUDE.md");
|
|
134
|
+
const newSection = buildClaudeMd(projectDir);
|
|
135
|
+
if (node_fs_1.default.existsSync(claudeMdPath)) {
|
|
136
|
+
const existing = await promises_1.default.readFile(claudeMdPath, "utf8");
|
|
137
|
+
if (existing.includes("DocuFlow")) {
|
|
138
|
+
// Already has DocuFlow section ā replace it
|
|
139
|
+
const withoutDocuflow = existing.replace(/\n?# DocuFlow[\s\S]*/, "").trimEnd();
|
|
140
|
+
await promises_1.default.writeFile(claudeMdPath, withoutDocuflow + "\n\n" + newSection, "utf8");
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Append to existing CLAUDE.md
|
|
144
|
+
await promises_1.default.appendFile(claudeMdPath, "\n\n" + newSection, "utf8");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
await promises_1.default.writeFile(claudeMdPath, newSection, "utf8");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
31
151
|
async function run() {
|
|
32
152
|
const configPath = getClaudeDesktopConfigPath();
|
|
33
153
|
const serverBin = resolveServerBin();
|
|
@@ -46,9 +166,28 @@ async function run() {
|
|
|
46
166
|
config.mcpServers.docuflow = { command: nodeBin, args: [serverBin] };
|
|
47
167
|
await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
|
|
48
168
|
await promises_1.default.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
49
|
-
// Create .docuflow/
|
|
50
|
-
const
|
|
169
|
+
// Create .docuflow/ directory structure
|
|
170
|
+
const projectDir = process.cwd();
|
|
171
|
+
const docuflowDir = node_path_1.default.join(projectDir, ".docuflow");
|
|
172
|
+
const specsDir = node_path_1.default.join(docuflowDir, "specs");
|
|
173
|
+
const wikiDir = node_path_1.default.join(docuflowDir, "wiki");
|
|
174
|
+
const sourcesDir = node_path_1.default.join(docuflowDir, "sources");
|
|
175
|
+
const entitiesDir = node_path_1.default.join(wikiDir, "entities");
|
|
176
|
+
const conceptsDir = node_path_1.default.join(wikiDir, "concepts");
|
|
177
|
+
const timelinesDir = node_path_1.default.join(wikiDir, "timelines");
|
|
178
|
+
const synthesesDir = node_path_1.default.join(wikiDir, "syntheses");
|
|
51
179
|
await promises_1.default.mkdir(specsDir, { recursive: true });
|
|
180
|
+
await promises_1.default.mkdir(entitiesDir, { recursive: true });
|
|
181
|
+
await promises_1.default.mkdir(conceptsDir, { recursive: true });
|
|
182
|
+
await promises_1.default.mkdir(timelinesDir, { recursive: true });
|
|
183
|
+
await promises_1.default.mkdir(synthesesDir, { recursive: true });
|
|
184
|
+
await promises_1.default.mkdir(sourcesDir, { recursive: true });
|
|
185
|
+
// Copy or create template files
|
|
186
|
+
await copyTemplateFile("schema.md", node_path_1.default.join(docuflowDir, "schema.md"));
|
|
187
|
+
await copyTemplateFile("index.md", node_path_1.default.join(docuflowDir, "index.md"));
|
|
188
|
+
await copyTemplateFile("log.md", node_path_1.default.join(docuflowDir, "log.md"));
|
|
189
|
+
// Generate CLAUDE.md so Claude Code picks up DocuFlow automatically
|
|
190
|
+
await writeClaudeMd(projectDir);
|
|
52
191
|
// Add .docuflow/ to .gitignore if present and not already listed
|
|
53
192
|
const gitignorePath = node_path_1.default.join(process.cwd(), ".gitignore");
|
|
54
193
|
if (node_fs_1.default.existsSync(gitignorePath)) {
|
|
@@ -57,11 +196,32 @@ async function run() {
|
|
|
57
196
|
await promises_1.default.appendFile(gitignorePath, "\n# Docuflow\n.docuflow/\n");
|
|
58
197
|
}
|
|
59
198
|
}
|
|
60
|
-
console.log("Docuflow initialised successfully.");
|
|
199
|
+
console.log("ā Docuflow initialised successfully.");
|
|
200
|
+
console.log("");
|
|
201
|
+
console.log("š Structure created:");
|
|
202
|
+
console.log(` ${docuflowDir}/`);
|
|
203
|
+
console.log(` āāā specs/ (for legacy specs)`);
|
|
204
|
+
console.log(` āāā wiki/ (LLM-generated pages)`);
|
|
205
|
+
console.log(` ā āāā entities/`);
|
|
206
|
+
console.log(` ā āāā concepts/`);
|
|
207
|
+
console.log(` ā āāā timelines/`);
|
|
208
|
+
console.log(` ā āāā syntheses/`);
|
|
209
|
+
console.log(` āāā sources/ (immutable raw files)`);
|
|
210
|
+
console.log(` āāā schema.md (wiki configuration)`);
|
|
211
|
+
console.log(` āāā index.md (auto-maintained catalog)`);
|
|
212
|
+
console.log(` āāā log.md (operation log)`);
|
|
213
|
+
console.log("");
|
|
214
|
+
console.log("š CLAUDE.md:");
|
|
215
|
+
console.log(` Generated at: ${node_path_1.default.join(projectDir, "CLAUDE.md")}`);
|
|
216
|
+
console.log(` Claude Code will automatically read DocuFlow tool instructions.`);
|
|
61
217
|
console.log("");
|
|
218
|
+
console.log("š§ MCP Configuration:");
|
|
62
219
|
console.log(` MCP key: mcpServers.docuflow`);
|
|
63
220
|
console.log(` Config file: ${configPath}`);
|
|
64
|
-
console.log(` Specs dir: ${specsDir}`);
|
|
65
221
|
console.log("");
|
|
66
|
-
console.log("
|
|
222
|
+
console.log("š Next steps:");
|
|
223
|
+
console.log(" 1. Edit .docuflow/schema.md to customize your wiki");
|
|
224
|
+
console.log(" 2. Add source files to .docuflow/sources/");
|
|
225
|
+
console.log(" 3. Use LLM Wiki tools to ingest, query, and maintain wiki");
|
|
226
|
+
console.log(" 4. Restart Claude Desktop to activate");
|
|
67
227
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.run = run;
|
|
|
7
7
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
11
|
function getClaudeDesktopConfigPath() {
|
|
11
12
|
const platform = process.platform;
|
|
12
13
|
if (platform === "darwin") {
|
|
@@ -17,8 +18,33 @@ function getClaudeDesktopConfigPath() {
|
|
|
17
18
|
}
|
|
18
19
|
return node_path_1.default.join(node_os_1.default.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
19
20
|
}
|
|
21
|
+
async function readLastIngestDate(docuDir) {
|
|
22
|
+
try {
|
|
23
|
+
const log = await promises_1.default.readFile(node_path_1.default.join(docuDir, "log.md"), "utf8");
|
|
24
|
+
const match = log.match(/\[(\d{4}-\d{2}-\d{2}[^\]]*)\]\s+ingest/);
|
|
25
|
+
return match ? match[1] : "never";
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return "never";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function countWikiPages(docuDir) {
|
|
32
|
+
const counts = { entities: 0, concepts: 0, timelines: 0, syntheses: 0 };
|
|
33
|
+
for (const cat of Object.keys(counts)) {
|
|
34
|
+
try {
|
|
35
|
+
const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "wiki", cat));
|
|
36
|
+
counts[cat] = files.filter((f) => f.endsWith(".md")).length;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Directory may not exist
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return counts;
|
|
43
|
+
}
|
|
20
44
|
async function run() {
|
|
21
45
|
const configPath = getClaudeDesktopConfigPath();
|
|
46
|
+
const projectDir = process.cwd();
|
|
47
|
+
const docuDir = node_path_1.default.join(projectDir, ".docuflow");
|
|
22
48
|
// Check MCP registration
|
|
23
49
|
let registered = false;
|
|
24
50
|
try {
|
|
@@ -31,21 +57,73 @@ async function run() {
|
|
|
31
57
|
}
|
|
32
58
|
// Count spec files
|
|
33
59
|
let specCount = 0;
|
|
34
|
-
const specsDir = node_path_1.default.join(process.cwd(), ".docuflow", "specs");
|
|
35
60
|
try {
|
|
36
|
-
const entries = await promises_1.default.readdir(
|
|
37
|
-
specCount = entries.filter(e => e.endsWith(".md")).length;
|
|
61
|
+
const entries = await promises_1.default.readdir(node_path_1.default.join(docuDir, "specs"));
|
|
62
|
+
specCount = entries.filter((e) => e.endsWith(".md")).length;
|
|
38
63
|
}
|
|
39
64
|
catch {
|
|
40
65
|
// .docuflow/specs doesn't exist
|
|
41
66
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
67
|
+
// Count sources
|
|
68
|
+
let sourceCount = 0;
|
|
69
|
+
try {
|
|
70
|
+
const entries = await promises_1.default.readdir(node_path_1.default.join(docuDir, "sources"));
|
|
71
|
+
sourceCount = entries.filter((e) => e.endsWith(".md") || e.endsWith(".txt")).length;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// .docuflow/sources doesn't exist
|
|
75
|
+
}
|
|
76
|
+
// Check for CLAUDE.md
|
|
77
|
+
const claudeMdExists = node_fs_1.default.existsSync(node_path_1.default.join(projectDir, "CLAUDE.md"));
|
|
78
|
+
// Count wiki pages per category
|
|
79
|
+
const wikiCounts = await countWikiPages(docuDir);
|
|
80
|
+
const totalWikiPages = Object.values(wikiCounts).reduce((a, b) => a + b, 0);
|
|
81
|
+
// Last ingest date
|
|
82
|
+
const lastIngest = await readLastIngestDate(docuDir);
|
|
83
|
+
// Get CLI version from package.json
|
|
84
|
+
let version = "unknown";
|
|
85
|
+
try {
|
|
86
|
+
const pkgPath = require.resolve("@doquflow/cli/package.json");
|
|
87
|
+
const pkg = JSON.parse(await promises_1.default.readFile(pkgPath, "utf8"));
|
|
88
|
+
version = pkg.version;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
try {
|
|
92
|
+
const pkgPath = node_path_1.default.resolve(__dirname, "..", "..", "package.json");
|
|
93
|
+
const pkg = JSON.parse(await promises_1.default.readFile(pkgPath, "utf8"));
|
|
94
|
+
version = pkg.version;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Can't resolve version
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log("\nDocuflow status");
|
|
101
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
102
|
+
console.log(` Version: ${version}`);
|
|
103
|
+
console.log(` MCP registered: ${registered ? "ā yes" : "ā no"}`);
|
|
104
|
+
console.log(` CLAUDE.md: ${claudeMdExists ? "ā present" : "ā missing"}`);
|
|
105
|
+
console.log(` Config file: ${configPath}`);
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log(" Specs written: " + specCount);
|
|
108
|
+
console.log(" Sources: " + sourceCount);
|
|
109
|
+
console.log(" Last ingest: " + lastIngest);
|
|
110
|
+
console.log("");
|
|
111
|
+
console.log(" Wiki pages: " + totalWikiPages + " total");
|
|
112
|
+
if (totalWikiPages > 0) {
|
|
113
|
+
console.log(` entities: ${wikiCounts.entities}`);
|
|
114
|
+
console.log(` concepts: ${wikiCounts.concepts}`);
|
|
115
|
+
console.log(` syntheses: ${wikiCounts.syntheses}`);
|
|
116
|
+
console.log(` timelines: ${wikiCounts.timelines}`);
|
|
117
|
+
}
|
|
118
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
47
119
|
if (!registered) {
|
|
48
|
-
console.log("");
|
|
49
|
-
|
|
120
|
+
console.log('\n ā Run "docuflow init" to register MCP server.');
|
|
121
|
+
}
|
|
122
|
+
if (!claudeMdExists) {
|
|
123
|
+
console.log('\n ā Run "docuflow init" to generate CLAUDE.md.');
|
|
124
|
+
}
|
|
125
|
+
if (totalWikiPages === 0 && sourceCount === 0) {
|
|
126
|
+
console.log('\n š” Run "docuflow suggest" to see what to document first.');
|
|
50
127
|
}
|
|
128
|
+
console.log();
|
|
51
129
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
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.run = run;
|
|
7
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const DOMAIN_SUGGESTIONS = {
|
|
11
|
+
"Code/Architecture": [
|
|
12
|
+
{ priority: 1, type: "wiki_page", title: "System Architecture Overview", reason: "High-level view of how all components fit together", prompt: "Ingest the README or architecture doc, then ask: 'Create a System Architecture Overview page in the wiki'" },
|
|
13
|
+
{ priority: 2, type: "wiki_page", title: "Core Architectural Patterns", reason: "Design patterns used throughout the codebase", prompt: "Ask Claude: 'Scan the codebase with list_modules and identify the core architectural patterns'" },
|
|
14
|
+
{ priority: 3, type: "wiki_page", title: "Module Dependencies", reason: "Which modules depend on which ā risky to change without knowing", prompt: "Ask Claude: 'Use list_modules to build a module dependency map and write it as a wiki page'" },
|
|
15
|
+
{ priority: 4, type: "wiki_page", title: "Database Schema Overview", reason: "Tables, relationships, and ownership by module", prompt: "Ask Claude: 'Scan the project for DB tables using list_modules and create a Database Schema wiki page'" },
|
|
16
|
+
{ priority: 5, type: "starter_question", title: "What modules are most connected?", reason: "Identifies the highest-risk code to change", prompt: "Ask Claude: 'Use list_modules on this project and tell me which files have the most dependencies'" },
|
|
17
|
+
],
|
|
18
|
+
Research: [
|
|
19
|
+
{ priority: 1, type: "wiki_page", title: "Research Domain Overview", reason: "Big picture of the research area", prompt: "Ingest your main survey paper, then ask: 'Create a Research Domain Overview page'" },
|
|
20
|
+
{ priority: 2, type: "wiki_page", title: "Key Findings & Synthesis", reason: "Major discoveries and insights", prompt: "Ask Claude: 'After ingesting my sources, synthesize the key findings into a wiki page'" },
|
|
21
|
+
{ priority: 3, type: "wiki_page", title: "Areas of Contradiction", reason: "Where researchers disagree ā critical to track", prompt: "Ask Claude: 'Run lint_wiki and identify any contradictions, then create an Areas of Contradiction page'" },
|
|
22
|
+
{ priority: 4, type: "wiki_page", title: "Open Research Questions", reason: "What's still unknown in this domain", prompt: "Ask Claude: 'Based on ingested sources, what questions remain unanswered?'" },
|
|
23
|
+
{ priority: 5, type: "starter_question", title: "What are the most cited entities?", reason: "Shows which concepts are most important", prompt: "Ask Claude: 'Which entities appear most often across all ingested sources?'" },
|
|
24
|
+
],
|
|
25
|
+
Business: [
|
|
26
|
+
{ priority: 1, type: "wiki_page", title: "Market Overview", reason: "Big picture of the market and competitive landscape", prompt: "Ingest market research docs, then ask: 'Create a Market Overview wiki page'" },
|
|
27
|
+
{ priority: 2, type: "wiki_page", title: "Competitive Analysis", reason: "How competitors differ ā essential strategic context", prompt: "Ask Claude: 'After ingesting competitor info, create a Competitive Analysis synthesis page'" },
|
|
28
|
+
{ priority: 3, type: "wiki_page", title: "Market Opportunities", reason: "Gaps and growth areas to track", prompt: "Ask Claude: 'Identify market opportunities from ingested sources and create a wiki page'" },
|
|
29
|
+
{ priority: 4, type: "wiki_page", title: "Key Players", reason: "Entities (companies, people) to track over time", prompt: "Ask Claude: 'Create an entity page for each key competitor found in sources'" },
|
|
30
|
+
{ priority: 5, type: "starter_question", title: "Who are the key players in this market?", reason: "First question to anchor the wiki", prompt: "Ask Claude: 'List the key companies and people from ingested market data'" },
|
|
31
|
+
],
|
|
32
|
+
Personal: [
|
|
33
|
+
{ priority: 1, type: "wiki_page", title: "Learning Goals", reason: "Anchors everything else in the wiki", prompt: "Ask Claude: 'Create a Learning Goals page from my notes in .docuflow/sources/'" },
|
|
34
|
+
{ priority: 2, type: "wiki_page", title: "Key Personal Insights", reason: "Preserve insights before they fade", prompt: "Ask Claude: 'Ingest my notes and create a Key Insights synthesis page'" },
|
|
35
|
+
{ priority: 3, type: "wiki_page", title: "Key Resources", reason: "Books, courses, links to revisit", prompt: "Ask Claude: 'Extract and create an entity page for each key resource mentioned in my notes'" },
|
|
36
|
+
{ priority: 4, type: "wiki_page", title: "Action Items & Next Steps", reason: "Turns knowledge into action", prompt: "Ask Claude: 'What action items can you find across my wiki pages? Create a synthesis page'" },
|
|
37
|
+
{ priority: 5, type: "starter_question", title: "What do I already know about this topic?", reason: "Baseline before adding more sources", prompt: "Ask Claude: 'Query the wiki for what I know about [your topic]'" },
|
|
38
|
+
],
|
|
39
|
+
General: [
|
|
40
|
+
{ priority: 1, type: "action", title: "Add your first source file", reason: "Nothing can be ingested until you put a file in .docuflow/sources/", prompt: "Copy your README, main doc, or any markdown file into .docuflow/sources/" },
|
|
41
|
+
{ priority: 2, type: "wiki_page", title: "Overview / Summary page", reason: "A starting page that links to everything else", prompt: "Ask Claude: 'Ingest my first source and create an overview synthesis page'" },
|
|
42
|
+
{ priority: 3, type: "starter_question", title: "What are the key entities in this domain?", reason: "Gets the wiki started with real content", prompt: "Ask Claude: 'After ingesting my first source, what are the key entities?'" },
|
|
43
|
+
{ priority: 4, type: "wiki_page", title: "Key Concepts", reason: "Important ideas worth preserving", prompt: "Ask Claude: 'Create concept pages for the top 5 ideas from ingested sources'" },
|
|
44
|
+
{ priority: 5, type: "action", title: "Run get_schema_guidance", reason: "DocuFlow will tell you what's missing based on your domain", prompt: "Ask Claude: 'Run get_schema_guidance on my project and show what pages are recommended'" },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
function detectDomain(schemaContent) {
|
|
48
|
+
if (/Architecture|Code|codebase|microservice|API/i.test(schemaContent))
|
|
49
|
+
return "Code/Architecture";
|
|
50
|
+
if (/Research|paper|findings|literature/i.test(schemaContent))
|
|
51
|
+
return "Research";
|
|
52
|
+
if (/Business|market|competitor|revenue/i.test(schemaContent))
|
|
53
|
+
return "Business";
|
|
54
|
+
if (/Personal|learning|goal|habit/i.test(schemaContent))
|
|
55
|
+
return "Personal";
|
|
56
|
+
return "General";
|
|
57
|
+
}
|
|
58
|
+
async function countWikiPages(docuDir) {
|
|
59
|
+
const counts = { entities: 0, concepts: 0, timelines: 0, syntheses: 0 };
|
|
60
|
+
for (const cat of Object.keys(counts)) {
|
|
61
|
+
try {
|
|
62
|
+
const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "wiki", cat));
|
|
63
|
+
counts[cat] = files.filter((f) => f.endsWith(".md")).length;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Directory may not exist
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return counts;
|
|
70
|
+
}
|
|
71
|
+
async function countSources(docuDir) {
|
|
72
|
+
try {
|
|
73
|
+
const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "sources"));
|
|
74
|
+
return files.filter((f) => f.endsWith(".md") || f.endsWith(".txt")).length;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function run() {
|
|
81
|
+
const projectDir = process.cwd();
|
|
82
|
+
const docuDir = node_path_1.default.join(projectDir, ".docuflow");
|
|
83
|
+
if (!node_fs_1.default.existsSync(docuDir)) {
|
|
84
|
+
console.log("ā DocuFlow not initialised in this directory.");
|
|
85
|
+
console.log(' Run "docuflow init" first.\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Detect domain from schema
|
|
89
|
+
let domain = "General";
|
|
90
|
+
try {
|
|
91
|
+
const schema = await promises_1.default.readFile(node_path_1.default.join(docuDir, "schema.md"), "utf8");
|
|
92
|
+
domain = detectDomain(schema);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// No schema yet ā fall back to General
|
|
96
|
+
}
|
|
97
|
+
const [pageCounts, sourceCount] = await Promise.all([
|
|
98
|
+
countWikiPages(docuDir),
|
|
99
|
+
countSources(docuDir),
|
|
100
|
+
]);
|
|
101
|
+
const totalPages = Object.values(pageCounts).reduce((a, b) => a + b, 0);
|
|
102
|
+
console.log(`\nš” DocuFlow Suggestions ā ${domain} domain\n`);
|
|
103
|
+
console.log(` Current wiki: ${totalPages} pages | ${sourceCount} source(s) ingested\n`);
|
|
104
|
+
if (totalPages === 0 && sourceCount === 0) {
|
|
105
|
+
console.log("š± Your wiki is empty. Here's where to start:\n");
|
|
106
|
+
}
|
|
107
|
+
else if (totalPages < 10) {
|
|
108
|
+
console.log("š Wiki is growing. Recommended next pages:\n");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log("ā
Good coverage. Here's what could be stronger:\n");
|
|
112
|
+
}
|
|
113
|
+
const suggestions = DOMAIN_SUGGESTIONS[domain] ?? DOMAIN_SUGGESTIONS.General;
|
|
114
|
+
for (const s of suggestions) {
|
|
115
|
+
const icon = s.type === "action" ? "ā¶" : s.type === "starter_question" ? "ā" : "š";
|
|
116
|
+
console.log(` ${s.priority}. ${icon} ${s.title}`);
|
|
117
|
+
console.log(` ${s.reason}`);
|
|
118
|
+
if (s.prompt) {
|
|
119
|
+
console.log(` ā ${s.prompt}`);
|
|
120
|
+
}
|
|
121
|
+
console.log();
|
|
122
|
+
}
|
|
123
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
124
|
+
console.log("Quick start prompts for Claude:\n");
|
|
125
|
+
console.log(` ⢠"Scan the project at ${projectDir} with list_modules"`);
|
|
126
|
+
console.log(` ⢠"Show me get_schema_guidance for ${projectDir}"`);
|
|
127
|
+
console.log(` ⢠"What wiki pages exist? Run list_wiki on ${projectDir}"`);
|
|
128
|
+
console.log(` ⢠"What should I document first in this project?"`);
|
|
129
|
+
console.log();
|
|
130
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -33,16 +33,26 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
|
-
const [, , cmd] = process.argv;
|
|
36
|
+
const [, , cmd, flag] = process.argv;
|
|
37
37
|
if (cmd === 'init') {
|
|
38
|
-
|
|
38
|
+
if (flag === '--interactive' || flag === '-i') {
|
|
39
|
+
Promise.resolve().then(() => __importStar(require('./commands/init-interactive'))).then(m => m.runInteractive());
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
Promise.resolve().then(() => __importStar(require('./commands/init'))).then(m => m.run());
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
else if (cmd === 'status') {
|
|
41
46
|
Promise.resolve().then(() => __importStar(require('./commands/status'))).then(m => m.run());
|
|
42
47
|
}
|
|
48
|
+
else if (cmd === 'suggest') {
|
|
49
|
+
Promise.resolve().then(() => __importStar(require('./commands/suggest'))).then(m => m.run());
|
|
50
|
+
}
|
|
43
51
|
else {
|
|
44
|
-
console.log('Usage: npx @doquflow/cli <
|
|
52
|
+
console.log('Usage: npx @doquflow/cli <command>');
|
|
45
53
|
console.log('');
|
|
46
|
-
console.log(' init
|
|
47
|
-
console.log('
|
|
54
|
+
console.log(' init Register Docuflow MCP and generate CLAUDE.md');
|
|
55
|
+
console.log(' init --interactive Interactive setup wizard (choose domain, project name)');
|
|
56
|
+
console.log(' status Show wiki health, page counts, and MCP status');
|
|
57
|
+
console.log(' suggest Show what to document first (domain-specific guidance)');
|
|
48
58
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doquflow/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI for setting up Docuflow in your project",
|
|
5
5
|
"author": "Docuflow <hello@doquflows.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"build": "tsc"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@doquflow/server": "0.
|
|
33
|
+
"@doquflow/server": "0.4.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|